Skip to content

Commit

Permalink
chore: add react router
Browse files Browse the repository at this point in the history
  • Loading branch information
justynoh committed May 15, 2023
1 parent c778c54 commit bd6fbe5
Show file tree
Hide file tree
Showing 3 changed files with 124 additions and 2 deletions.
7 changes: 5 additions & 2 deletions src/app/loaders/express/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@ import { AuthRouter } from '../../modules/auth/auth.routes'
import { ExamplesRouter } from '../../modules/examples/examples.routes'
import { AdminFormsRouter } from '../../modules/form/admin-form/admin-form.routes'
import { PublicFormRouter } from '../../modules/form/public-form/public-form.routes'
import { FrontendRouter } from '../../modules/frontend/frontend.routes'
import { FrontendRouter as OldFrontendRouter } from '../../modules/frontend/frontend.routes'
import { FrontendRouter } from '../../modules/frontend-new/frontend.routes'
import { MYINFO_ROUTER_PREFIX } from '../../modules/myinfo/myinfo.constants'
import { MyInfoRouter } from '../../modules/myinfo/myinfo.routes'
import { SgidRouter } from '../../modules/sgid/sgid.routes'
Expand Down Expand Up @@ -112,7 +113,7 @@ const loadExpressApp = async (connection: Connection) => {
app.use(IntranetMiddleware.logIntranetUsage)

// Deprecated routes
app.use('/frontend', FrontendRouter)
app.use('/frontend', OldFrontendRouter)
app.use('/auth', AuthRouter)
app.use('/transaction', VfnRouter)
app.use('/examples', ExamplesRouter)
Expand Down Expand Up @@ -144,6 +145,8 @@ const loadExpressApp = async (connection: Connection) => {
// not served statically above will also return 404
app.get(/^\/[^/]+\.[a-z]+$/, catchNonExistentStaticRoutesMiddleware)

app.use('/', FrontendRouter)

app.use(sentryMiddlewares())
app.use(errorHandlerMiddlewares())

Expand Down
107 changes: 107 additions & 0 deletions src/app/modules/frontend-new/frontend.controller.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
import { readFileSync } from 'fs'
import { escape } from 'html-escaper'
import { get } from 'lodash'
import path from 'path'

import { FormStatus } from '../../../../shared/types'
import { createLoggerWithLabel } from '../../config/logger'
import { ControllerHandler } from '../core/core.types'
import * as FormService from '../form/form.service'
import { createMetatags } from '../form/public-form/public-form.service'
import { RedirectParams } from '../form/public-form/public-form.types'

const logger = createLoggerWithLabel(module)

const reactFrontendPath = path.resolve('dist/frontend')
const reactHtml = readFileSync(path.join(reactFrontendPath, 'index.html'), {
encoding: 'utf8',
})

type MetaTags = {
title: string
description: string
image: string
}
const replaceWithMetaTags = ({
title,
description,
image,
}: MetaTags): string => {
return reactHtml
.replace(/(__OG_TITLE__)/g, escape(title))
.replace(/(__OG_DESCRIPTION__)/g, escape(description))
.replace(/(__OG_IMAGE__)/g, escape(image))
}

const serveFormReact =
(isPublicForm: boolean): ControllerHandler =>
async (req, res) => {
let tags: MetaTags = {
title: 'FormSG',
description: 'Trusted form manager of the Singapore Government',
image: 'og-img-metatag-nonpublicform.png',
}

if (isPublicForm && get(req.params, 'formId')) {
tags = await getPublicFormMetaTags(get(req.params, 'formId') ?? '')
}

const reactHtmlWithMetaTags = replaceWithMetaTags(tags)

return (
res
// Prevent index.html from being cached by browsers.
.setHeader('Cache-Control', 'no-cache')
.send(reactHtmlWithMetaTags)
)
}

const getPublicFormMetaTags = async (formId: string): Promise<MetaTags> => {
const createMetatagsResult = await createMetatags({
formId,
})

if (createMetatagsResult.isErr()) {
logger.error({
message: 'Error fetching metatags',
meta: {
action: 'getPublicFormMetaTags',
formId,
},
error: createMetatagsResult.error,
})
return {
title: 'FormSG',
description: '',
image: 'og-img-metatag-publicform.png',
}
} else {
const { title, description } = createMetatagsResult.value
return {
title: title,
description: description ?? '',
image: 'og-img-metatag-publicform.png',
}
}
}

export const servePublicForm: ControllerHandler<
RedirectParams,
unknown,
unknown,
Record<string, string>
> = async (req, res, next) => {
const formResult = await FormService.retrieveFormKeysById(req.params.formId, [
'responseMode',
'status',
])

const isPublicForm =
!formResult.isErr() && formResult.value.status === FormStatus.Public

return serveFormReact(isPublicForm)(req, res, next)
}

export const serveDefault: ControllerHandler = (req, res, next) => {
return serveFormReact(/* isPublic= */ false)(req, res, next)
}
12 changes: 12 additions & 0 deletions src/app/modules/frontend-new/frontend.routes.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { Router } from 'express'

import * as FrontendController from './frontend.controller'

export const FrontendRouter = Router()

FrontendRouter.get(
'/:formId([a-fA-F0-9]{24})',
FrontendController.servePublicForm,
)

FrontendRouter.get('*', FrontendController.serveDefault)

0 comments on commit bd6fbe5

Please sign in to comment.