feat: refactored server to use ts and improved folder structure

This commit is contained in:
Roberto Tonino
2021-04-03 19:46:54 +02:00
parent f98abb384c
commit 8e4e2ff5eb
43 changed files with 4125 additions and 452 deletions

38
server/src/app.ts Normal file
View File

@@ -0,0 +1,38 @@
import http from 'http'
import express, { Application } from 'express'
import initDebug from 'debug'
import { registerMiddlewares } from './middlewares'
import indexRouter from './routes'
import usersRouter from './routes/users'
import { normalizePort } from './helpers/port'
import { getErrorCb, getListeningCb } from './helpers/server-callbacks'
import { registerApis } from './routes/api/register'
const PORT = normalizePort(process.env.PORT || '6595')
const debug = initDebug('deemix-gui:server')
const app: Application = express()
const server = http.createServer(app)
/* === Middlewares === */
registerMiddlewares(app)
/* === Routes === */
app.use('/', indexRouter)
app.use('/users', usersRouter)
/* === APIs === */
registerApis(app)
/* === Config === */
app.set('port', PORT)
/* === Server port === */
server.listen(PORT)
/* === Server callbacks === */
server.on('error', getErrorCb(PORT))
server.on('listening', getListeningCb(server, debug))

View File

@@ -0,0 +1,4 @@
import path from 'path'
export const ROOT_DIR = path.resolve('../')
export const WEBUI_DIR = path.join(ROOT_DIR, 'webui', 'public')

View File

@@ -0,0 +1,22 @@
import { Port } from '../types'
/**
* Normalize a port into a number, string, or false.
*
* @since 0.0.0
*/
export function normalizePort(portString: string): Port {
const port = parseInt(portString, 10)
if (isNaN(port)) {
// named pipe
return portString
}
if (port >= 0) {
// port number
return port
}
return false
}

View File

@@ -0,0 +1,47 @@
import http from 'http'
import type { Debugger } from 'debug'
/**
* Event listener for HTTP server "error" event.
*
* @since 0.0.0
*/
export function getErrorCb(port: number | string | boolean) {
return (error: any) => {
if (error.syscall !== 'listen') {
throw error
}
const bind = typeof port === 'string' ? 'Pipe ' + port : 'Port ' + port
// handle specific listen errors with friendly messages
switch (error.code) {
case 'EACCES': {
console.error(bind + ' requires elevated privileges')
process.exit(1)
}
case 'EADDRINUSE': {
console.error(bind + ' is already in use')
process.exit(1)
}
default:
throw error
}
}
}
/**
* Event listener for HTTP server "listening" event.
*
* @since 0.0.0
*/
export function getListeningCb(server: http.Server, debug: Debugger) {
return () => {
const addr = server.address()
if (addr) {
const bind = typeof addr === 'string' ? 'pipe ' + addr : 'port ' + addr.port
debug('Listening on ' + bind)
}
}
}

13
server/src/middlewares.ts Normal file
View File

@@ -0,0 +1,13 @@
import type { Application } from 'express'
import logger from 'morgan'
import express from 'express'
import cookieParser from 'cookie-parser'
import { WEBUI_DIR } from './helpers/paths'
export function registerMiddlewares(app: Application) {
app.use(logger('dev'))
app.use(express.json())
app.use(express.urlencoded({ extended: false }))
app.use(cookieParser())
app.use(express.static(WEBUI_DIR))
}

View File

@@ -0,0 +1,3 @@
import { ApiHandler } from '../../../types'
export default [] as ApiHandler[]

View File

@@ -0,0 +1,3 @@
import sample from './sample'
export default [sample]

View File

@@ -0,0 +1,11 @@
import { ApiHandler } from '../../../types'
const path: ApiHandler['path'] = '/sample'
const handler: ApiHandler['handler'] = (_, res) => {
res.send('Mandi')
}
const apiHandler: ApiHandler = { path, handler }
export default apiHandler

View File

@@ -0,0 +1,3 @@
import { ApiHandler } from '../../../types'
export default [] as ApiHandler[]

View File

@@ -0,0 +1,3 @@
import { ApiHandler } from '../../../types'
export default [] as ApiHandler[]

View File

@@ -0,0 +1,42 @@
import type { Application } from 'express'
import type { ApiHandler } from '../../types'
import getEndpoints from './get'
import deleteEndpoints from './delete'
import postEndpoints from './post'
import patchEndpoints from './patch'
const prependApiPath = (path: string) => `/api${path}`
interface Method {
method: string
endpoints: ApiHandler[]
}
const methods: Method[] = [
{
method: 'get',
endpoints: getEndpoints
},
{
method: 'delete',
endpoints: deleteEndpoints
},
{
method: 'post',
endpoints: postEndpoints
},
{
method: 'patch',
endpoints: patchEndpoints
}
]
export function registerApis(app: Application) {
methods.forEach(({ method, endpoints }) => {
endpoints.forEach(endpoint => {
// @ts-ignore
app[method](prependApiPath(endpoint.path), endpoint.handler)
})
})
}

View File

@@ -0,0 +1,14 @@
import express from 'express'
const router = express.Router()
/**
* GET home page
*
* @since 0.0.0
*/
router.get('/', (_, res) => {
res.render('index', { title: 'Express' })
})
export default router

View File

@@ -0,0 +1,14 @@
import express from 'express'
const router = express.Router()
/**
* GET users listing.
*
* @since 0.0.0
*/
router.get('/', (_, res) => {
res.send('respond with a resource')
})
export default router

8
server/src/types.ts Normal file
View File

@@ -0,0 +1,8 @@
import { RequestHandler } from 'express'
export type Port = number | string | boolean
export interface ApiHandler {
path: string
handler: RequestHandler
}