Server.ts 4.62 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
import cookieParser from 'cookie-parser'
import morgan from 'morgan'
import path from 'path'
import helmet from 'helmet'
import cors from 'cors'

import express, { NextFunction, Request, Response } from 'express'
import StatusCodes from 'http-status-codes'
import 'express-async-errors'

import BaseRouter from './routes'
import logger from '@shared/Logger'

import { MongoClient } from 'mongodb'
import { chain } from 'stream-chain'
import * as fs from 'fs'
import { parser } from 'stream-json'
import { streamArray } from 'stream-json/streamers/StreamArray'
import { IBikeTrip } from '@entities/BikeTrip'


/************************************************************************************
 *                              Fill database and open connection
 ***********************************************************************************/

export const dbName = 'bikesharing'
const url = `mongodb://localhost:27017/${dbName}`

export const dbClient = new MongoClient(url, { useNewUrlParser: true, useUnifiedTopology: true })

export const bikeTripsCollectionName = 'biketrips';

(async () => {
    try {
        console.log("Attempting to connect to MongoDB server.")
        await dbClient.connect()
        console.log("Connected to MongoDB server.")

        const db = dbClient.db(dbName)
        const bikeTripCollection = await db.collection(bikeTripsCollectionName)
        const stats = await bikeTripCollection.stats()

        if(stats.count === 0){
            // create ascending index on fields startDate and endDate
            bikeTripCollection.createIndex({ startDate : 1 })
            bikeTripCollection.createIndex({ endDate : 1 })

            const dataStreamFromFile = fs.createReadStream('src/shared/data/bike-sharing-trip-data-4-january-28-february-reduced.json')

            const pipeline = chain([
                dataStreamFromFile,
                parser(),
                streamArray(),
            ])

            let bikeTripsTemp: IBikeTrip[] = []

            const startTime = Date.now()
            console.log('Inserting bike trips into database, please wait...')

            pipeline.on('data', async bikeTripsChunk => {
                bikeTripsTemp.push(bikeTripsChunk.value)

                if(bikeTripsTemp.length === 50000){
                    dataStreamFromFile.pause()
                    await bikeTripCollection.insertMany(bikeTripsTemp)
                    bikeTripsTemp = []
                    console.log(`...${(await bikeTripCollection.stats()).count} documents in database...`)
                    dataStreamFromFile.resume()
                }
            })

            pipeline.on('end', async () => {
                await bikeTripCollection.insertMany(bikeTripsTemp)

                const bikeTripCollectionStats = await bikeTripCollection.stats()
                console.log(`Database inserts done! Added ${bikeTripCollectionStats.count} bike trip documents to database, in ${(Date.now() - startTime)/1000} seconds.`)
            })

        } else {
            console.log(`Found ${stats.count} bike trip documents in database`)
        }
    } catch (err) {
        console.log(err.stack)
    }
})()


/************************************************************************************
 *                              Set basic express settings
 ***********************************************************************************/

const app = express()
const { BAD_REQUEST } = StatusCodes

app.use(express.json())
app.use(express.urlencoded({extended: true}))
app.use(cookieParser())

// Show routes called in console during development
if (process.env.NODE_ENV === 'development') {
    app.use(morgan('dev'))
}

// Allow cors
// eslint-disable-next-line @typescript-eslint/no-unsafe-call
app.use(cors())

// Security
if (process.env.NODE_ENV === 'production') {
    app.use(helmet())
}

// Add APIs
app.use('/api', BaseRouter)

// Print API errors
// eslint-disable-next-line @typescript-eslint/no-unused-vars
app.use((err: Error, req: Request, res: Response, next: NextFunction) => {
    logger.err(err, true)
    return res.status(BAD_REQUEST).json({
        error: err.message,
    })
})


/************************************************************************************
 *                              Serve front-end content
 ***********************************************************************************/

const dir = path.join(__dirname, 'frontend-build')
app.set('views', dir)

// middleware is needed to make express serve static CSS and Javascript files
app.use(express.static(dir))

app.get('/*', (req: Request, res: Response) => {
    res.sendFile('index.html', {root: dir})
})

// Export express instance
export default app