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