Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add local ssl #124

Merged
merged 2 commits into from
Jul 4, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion apps/front/.env
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@

# ------------------------------------------------------------------------------ BASE

# base start & end with slash: /{base}/
VITE_APP_BASE=/
# protocol for server: http or https (default: http)
PROTOCOL=http

# ------------------------------------------------------------------------------ DOCKER

Expand Down
23 changes: 23 additions & 0 deletions apps/front/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
- [scaffold](#scaffold)
- [generate](#generate)
- [Vite plugins](#vite-plugins)
- [Setup local SSL](#setup-local-ssl)

## About

Expand Down Expand Up @@ -200,6 +201,28 @@ Second part of the configuration is defined from [config/config.js](config/confi
htaccessTemplateFilePath: resolve("src/.htaccess")
```

## Setup local SSL

- Generate a self-signed certificate from `apps/front` root folder:

```shell
sudo openssl req -x509 -newkey rsa:4096 -keyout key.pem -out cert.pem -days 365 -nodes
```

- Trust the certificate:

```shell
sudo security add-trusted-cert -d -r trustRoot -k /Library/Keychains/System.keychain cert.pem
```

- Update .env var `PROTOCOL` to `https`

```dotenv
PROTOCOL=https
```

When you run `npm run dev`, you should see the app running on https://localhost:3000

## Credits

Developed by [cher-ami](https://github.com/cher-ami) team.
Expand Down
157 changes: 96 additions & 61 deletions apps/front/server.js
Original file line number Diff line number Diff line change
@@ -1,77 +1,112 @@
import * as React from "react"
import debug from "@wbe/debug"
import express from "express"
import { createServer } from "vite"
import * as https from "https"
import * as mfs from "@wbe/mfs"
import portFinderSync from "portfinder-sync"
import config from "./config/config.js"
import { renderToPipeableStream } from "react-dom/server"
import debug from "@wbe/debug"
import { createServer, loadEnv } from "vite"
import config from "./config/config.js"

const log = debug("server:server")

const loadEnvVars = loadEnv(process.env.NODE_ENV, process.cwd(), "")
const isProduction = process.env.NODE_ENV === "production"
const port = process.env.DOCKER_NODE_PORT ?? portFinderSync.getPort(3000)
const protocol = loadEnvVars.PROTOCOL ?? "http"
const isSSL = protocol === "https"

/**
* Dev server
*
*
*/
async function createDevServer() {
const app = express()

// dev script to inject
const devScripts = {
js: [{ tag: "script", attr: { type: "module", src: "/src/index.tsx" } }],
;(async () => {
// Get cert and key for https
let key, cert
if (isSSL) {
if (!(await mfs.fileExists("key.pem")) || !(await mfs.fileExists("cert.pem"))) {
console.error(
"You need to generate a key and a cert file with openssl in the apps/front/ directory. Follow the README documentation 'setup-local-ssl'."
)
process.exit(1)
}
key = await mfs.readFile("key.pem")
cert = await mfs.readFile("cert.pem")
}

// Create Vite server in middleware mode.
// This disables Vite's own HTML serving logic and let the parent server take control.
// https://vitejs.dev/config/server-options.html#server-middlewaremode
const vite = await createServer({
logLevel: "info",
server: { middlewareMode: true },
appType: "custom",
})
/**
* Dev server
*
*
*/
async function createDevServer() {
const app = express()

// use vite's connect instance as middleware
app.use(vite.middlewares)
app.use("*", async (req, res, next) => {
try {
// Transforms the ESM source code to be usable in Node.js
const { render } = await vite.ssrLoadModule(
`${config.srcDir}/server/index-server.tsx`
)
// Get react-dom from the render method
const dom = await render(req.originalUrl, devScripts, false)
// Create stream with renderToPipeableStream to support Suspense API
const stream = renderToPipeableStream(dom, {
onShellReady() {
res.setHeader("Content-type", "text/html")
res.statusCode = 200
stream.pipe(res)
},
onError(e) {
res.statusCode = 500
console.error(e)
},
// dev script to inject
const devScripts = {
js: [{ tag: "script", attr: { type: "module", src: "/src/index.tsx" } }],
}

// Create Vite server in middleware mode.
// This disables Vite's own HTML serving logic and let the parent server take control.
// https://vitejs.dev/config/server-options.html#server-middlewaremode
const vite = await createServer({
logLevel: "info",
server: {
middlewareMode: true,
https: (isSSL && { key, cert }) || false,
cors: false,
},
appType: "custom",
})

// use vite's connect instance as middleware
app.use(vite.middlewares)
app.use("*", async (req, res, next) => {
try {
// Transforms the ESM source code to be usable in Node.js
const { render } = await vite.ssrLoadModule(
`${config.srcDir}/server/index-server.tsx`
)
// Get react-dom from the render method
const dom = await render(req.originalUrl, devScripts, false)
// Create stream with renderToPipeableStream to support Suspense API
const stream = renderToPipeableStream(dom, {
onShellReady() {
res.setHeader("Content-type", "text/html")
res.statusCode = 200
stream.pipe(res)
},
onError(e) {
res.statusCode = 500
console.error(e)
},
})
} catch (e) {
vite.ssrFixStacktrace(e)
next(e)
}
})

let sslServer
if (isSSL) {
sslServer = https.createServer({ key, cert }, app)
sslServer.on("error", (error) => {
log(`Error on server: ${error}`)
})
} catch (e) {
vite.ssrFixStacktrace(e)
next(e)
}
})
return { app, vite }
}

/**
* Production server
* TODO
*
*
*/
async function createProdServer() {}
// return vite, app and server
return { vite, app, sslServer }
}

/**
* Let's go!
*/
/**
* Production server
* TODO
*
*
*/
async function createProdServer() {}

;(isProduction ? createProdServer : createDevServer)().then(({ app }) => app.listen(port))
/**
* Let's go!
*/
;(isProduction ? createProdServer : createDevServer)().then(({ app, sslServer }) =>
(sslServer ?? app).listen(port)
)
})()
5 changes: 3 additions & 2 deletions apps/front/vite.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,12 @@ const log = debug("config:vite.config")
export default defineConfig(({ command, mode }: ConfigEnv): UserConfig => {
const isDevelopment = mode === "development"
const ipAddress = ip.address()
const protocol: "http" | "https" = "http"

// get env variables from selected .env (depend of mode)
const loadEnvVars = loadEnv(mode, process.cwd(), "")

const protocol: "http" | "https" = (loadEnvVars.PROTOCOL as "http" | "https") ?? "http"

// merge loadEnv selected by vite in process.env
process.env = {
...loadEnvVars,
Expand Down Expand Up @@ -67,7 +68,7 @@ export default defineConfig(({ command, mode }: ConfigEnv): UserConfig => {
cors: true,
host: true,
port: process.env.PORT as any,
https: process.env.PROTOCOL === "https",
https: protocol === "https",
origin: `${protocol}://${process.env.HOST}:${process.env.PORT}`,
watch: {
// do not watch .env files to avoid reloading when build-dotenv is processed
Expand Down