<commit_before>import * as express from 'express'
import * as compression from 'compression'
import * as bodyParser from 'body-parser'
import * as cheerio from 'cheerio'
import verifier from 'google-id-token-verifier'
import * as favicon from 'serve-favicon'
import device from 'device'
import { renderToString } from 'react-dom/server'
import { Helmet } from 'react-helmet'
import pmx from 'pmx'
import { preloadAll } from 'react-loadable'

import * as http from 'http'
import * as fs from 'fs'
import * as path from 'path'
import * as qs from 'querystring'

import renderer from '../shared/renderer'

import routes from './routes'
import { Account } from './Account'
import { Course } from './Course'
import { API } from './scripts/api'
import { Database } from './Database'
import { DiscordBot } from './Discord'

import { clientId, cookie as cookieCredentials } from './scripts/credentials'
import { log } from './scripts/util'

const cookieSession = require('cookie-session')

export const Bot = new DiscordBot()

const usersPerDay = pmx.probe().meter({
  name: 'users/day',
  samples: 86400,
  timeframe: 86400
})

const downloadMetrics = {
  downloadsPerDay: pmx.probe().meter({
    name: 'Downloads/day',
    samples: 86400,
    timeframe: 86400
  }),
  downloadsWiiUPerDay: pmx.probe().meter({
    name: 'Downloads WiiU/day',
    samples: 86400,
    timeframe: 86400
  }),
  downloads3DSPerDay: pmx.probe().meter({
    name: 'Downloads 3DS/day',
    samples: 86400,
    timeframe: 86400
  }),
  downloadsProtoPerDay: pmx.probe().meter({
    name: 'Downloads Proto/day',
    samples: 86400,
    timeframe: 86400
  }),
  downloads64PerDay: pmx.probe().meter({
    name: 'Downloads 64/day',
    samples: 86400,
    timeframe: 86400
  })
}

const $index = cheerio.load(fs.readFileSync(path.join(__dirname, '../client/index.html'), {
  encoding: 'utf8'
}))

export const cacheMaxAgeImg = '7d'
export const cacheMaxAgeCSS = '1d'
export const cacheMaxAgeJS = '1y';

// initialize database
(async (): Promise<void> => {
  try {
    await Database.initialize()
    await main()
  } catch (err) {
    console.log(err)
  }
})()

async function main (): Promise<void> {
  console.log()
  log('Database initialized')

  Bot.login()

  // initialize app engine
  const app = express()

  const server = http.createServer(app)

  app.set('trust proxy', 1)
  app.use(compression())
  app.use(bodyParser.json({
    limit: '500kb'
  }))
  app.use(bodyParser.raw({
    limit: '6mb'
  }))
  app.use(favicon(path.join(__dirname, '../../favicon.ico')))
  app.use('/img', express.static(path.join(__dirname, '../static/images'), { maxAge: cacheMaxAgeImg }))
  app.use('/styles', express.static(path.join(__dirname, '../static/styles'), { maxAge: cacheMaxAgeCSS }))
  app.use('/scripts', express.static(path.join(__dirname, '../client/scripts'), { maxAge: cacheMaxAgeJS }))
  app.use(cookieSession({
    name: 'session',
    keys: cookieCredentials,
    maxAge: 24 * 60 * 60 * 1000
  }))
  const connections: any = {}
  app.use((req, res, next): void => {
    const ip = req.ip
    if (!connections.hasOwnProperty(ip)) {
      usersPerDay.mark()
      connections[ip] = {}
      setTimeout((): void => {
        delete connections[ip]
      }, 86400 * 1000)
    }
    next()
  })

  if (process.env.NODE_ENV === 'development') {
    app.use((req, res, next): void => {
      res.set('Access-Control-Allow-Origin', 'http://localhost:8080')
      next()
    })
  }

  app.use('/api/v2', routes)

  app.use('/courseimg/:id*?', async (req, res): Promise<void> => {
    const [id, full] = req.params.id.split('.')[0].split('_')
    if (id == null) {
      res.status(404).send('No course ID specified')
      return
    }
    try {
      const acceptHeader = req.get('accept')
      const acceptWebp = acceptHeader && acceptHeader.includes('image/webp')
      const img = await Database.getImage(id, !!full, !!acceptWebp)
      if (img == null) {
        res.status(404).send(`Course with ID ${id} has no image`)
        return
      }
      res.set('Content-Type', acceptWebp ? 'image/webp' : 'image/jpeg')
      res.set('Cache-Control', `public, max-age=${cacheMaxAgeImg}`)
      res.send(img)
    } catch (err) {
      res.status(500).send(`Internal Server Error:\n${err}`)
    }
  })

  app.use('/course64img/:id*?', async (req, res): Promise<void> => {
    const id = req.params.id
    if (id == null) {
      res.status(404).send('No course ID specified')
      return
    }
    try {
      const img = await Database.getImage64(id)
      if (img == null) {
        res.status(404).send(`Course with ID ${id} has no image`)
        return
      }
      res.set('Content-Type', 'image/jpeg')
      res.set('Cache-Control', `public, max-age=${cacheMaxAgeImg}`)
      res.send(img)
    } catch (err) {
      res.status(500).send(`Internal Server Error:\n${err}`)
    }
  })

  app.route('/tokensignin').post((req, res): void => {
    res.set('Access-Control-Allow-Headers', 'content-type')
    let idToken = req.body.tokenObj.id_token
    if (!idToken) {
      res.status(400).send('idToken not found')
    } else {
      verifier.verify(idToken, clientId, async (err: Error, tokenInfo: any): Promise<void> => {
        if (err) {
          res.status(400).send('idToken not verified')
        }

        // create account if it does not exist
        let googleId = tokenInfo.sub

        let account = await Account.getAccountByGoogleId(googleId, false)
        if (!account) {
          // create new account
          try {
            account = await Account.createAccount({
              googleid: googleId,
              username: tokenInfo.email.split('@')[0],
              email: tokenInfo.email,
              idtoken: idToken
            })
          } catch (err) {
            console.error(err)
          }
        } else {
          await Account.login(account._id, idToken)
        }
        req.session.idtoken = idToken
        res.json(account)
      })
    }
  })

  app.route('/signin').post(async (req, res): Promise<void> => {
    if (!req.session.idtoken) {
      res.status(400).send('No idToken submitted. Have you enabled cookies?')<commit_msg>fix token signin bad auth<commit_after>import * as express from 'express'
import * as compression from 'compression'
import * as bodyParser from 'body-parser'
import * as cheerio from 'cheerio'
import verifier from 'google-id-token-verifier'
import * as favicon from 'serve-favicon'
import device from 'device'
import { renderToString } from 'react-dom/server'
import { Helmet } from 'react-helmet'
import pmx from 'pmx'
import { preloadAll } from 'react-loadable'

import * as http from 'http'
import * as fs from 'fs'
import * as path from 'path'
import * as qs from 'querystring'

import renderer from '../shared/renderer'

import routes from './routes'
import { Account } from './Account'
import { Course } from './Course'
import { API } from './scripts/api'
import { Database } from './Database'
import { DiscordBot } from './Discord'

import { clientId, cookie as cookieCredentials } from './scripts/credentials'
import { log } from './scripts/util'

const cookieSession = require('cookie-session')

export const Bot = new DiscordBot()

const usersPerDay = pmx.probe().meter({
  name: 'users/day',
  samples: 86400,
  timeframe: 86400
})

const downloadMetrics = {
  downloadsPerDay: pmx.probe().meter({
    name: 'Downloads/day',
    samples: 86400,
    timeframe: 86400
  }),
  downloadsWiiUPerDay: pmx.probe().meter({
    name: 'Downloads WiiU/day',
    samples: 86400,
    timeframe: 86400
  }),
  downloads3DSPerDay: pmx.probe().meter({
    name: 'Downloads 3DS/day',
    samples: 86400,
    timeframe: 86400
  }),
  downloadsProtoPerDay: pmx.probe().meter({
    name: 'Downloads Proto/day',
    samples: 86400,
    timeframe: 86400
  }),
  downloads64PerDay: pmx.probe().meter({
    name: 'Downloads 64/day',
    samples: 86400,
    timeframe: 86400
  })
}

const $index = cheerio.load(fs.readFileSync(path.join(__dirname, '../client/index.html'), {
  encoding: 'utf8'
}))

export const cacheMaxAgeImg = '7d'
export const cacheMaxAgeCSS = '1d'
export const cacheMaxAgeJS = '1y';

// initialize database
(async (): Promise<void> => {
  try {
    await Database.initialize()
    await main()
  } catch (err) {
    console.log(err)
  }
})()

async function main (): Promise<void> {
  console.log()
  log('Database initialized')

  Bot.login()

  // initialize app engine
  const app = express()

  const server = http.createServer(app)

  app.set('trust proxy', 1)
  app.use(compression())
  app.use(bodyParser.json({
    limit: '500kb'
  }))
  app.use(bodyParser.raw({
    limit: '6mb'
  }))
  app.use(favicon(path.join(__dirname, '../../favicon.ico')))
  app.use('/img', express.static(path.join(__dirname, '../static/images'), { maxAge: cacheMaxAgeImg }))
  app.use('/styles', express.static(path.join(__dirname, '../static/styles'), { maxAge: cacheMaxAgeCSS }))
  app.use('/scripts', express.static(path.join(__dirname, '../client/scripts'), { maxAge: cacheMaxAgeJS }))
  app.use(cookieSession({
    name: 'session',
    keys: cookieCredentials,
    maxAge: 24 * 60 * 60 * 1000
  }))
  const connections: any = {}
  app.use((req, res, next): void => {
    const ip = req.ip
    if (!connections.hasOwnProperty(ip)) {
      usersPerDay.mark()
      connections[ip] = {}
      setTimeout((): void => {
        delete connections[ip]
      }, 86400 * 1000)
    }
    next()
  })

  if (process.env.NODE_ENV === 'development') {
    app.use((req, res, next): void => {
      res.set('Access-Control-Allow-Origin', 'http://localhost:8080')
      next()
    })
  }

  app.use('/api/v2', routes)

  app.use('/courseimg/:id*?', async (req, res): Promise<void> => {
    const [id, full] = req.params.id.split('.')[0].split('_')
    if (id == null) {
      res.status(404).send('No course ID specified')
      return
    }
    try {
      const acceptHeader = req.get('accept')
      const acceptWebp = acceptHeader && acceptHeader.includes('image/webp')
      const img = await Database.getImage(id, !!full, !!acceptWebp)
      if (img == null) {
        res.status(404).send(`Course with ID ${id} has no image`)
        return
      }
      res.set('Content-Type', acceptWebp ? 'image/webp' : 'image/jpeg')
      res.set('Cache-Control', `public, max-age=${cacheMaxAgeImg}`)
      res.send(img)
    } catch (err) {
      res.status(500).send(`Internal Server Error:\n${err}`)
    }
  })

  app.use('/course64img/:id*?', async (req, res): Promise<void> => {
    const id = req.params.id
    if (id == null) {
      res.status(404).send('No course ID specified')
      return
    }
    try {
      const img = await Database.getImage64(id)
      if (img == null) {
        res.status(404).send(`Course with ID ${id} has no image`)
        return
      }
      res.set('Content-Type', 'image/jpeg')
      res.set('Cache-Control', `public, max-age=${cacheMaxAgeImg}`)
      res.send(img)
    } catch (err) {
      res.status(500).send(`Internal Server Error:\n${err}`)
    }
  })

  app.route('/tokensignin').post((req, res): void => {
    res.set('Access-Control-Allow-Headers', 'content-type')
    let idToken = req.body.tokenObj.id_token
    if (!idToken) {
      res.status(400).send('idToken not found')
    } else {
      verifier.verify(idToken, clientId, async (err: Error, tokenInfo: any): Promise<void> => {
        if (err) {
          res.status(400).send('idToken not verified')
          return
        }

        // create account if it does not exist
        let googleId = tokenInfo.sub

        let account = await Account.getAccountByGoogleId(googleId, false)
        if (!account) {
          // create new account
          try {
            account = await Account.createAccount({
              googleid: googleId,
              username: tokenInfo.email.split('@')[0],
              email: tokenInfo.email,
              idtoken: idToken
            })
          } catch (err) {
            console.error(err)
          }
        } else {
          await Account.login(account._id, idToken)
        }
        req.session.idtoken = idToken
        res.json(account)
      })
    }
  })

  app.route('/signin').post(async (req, res): Promise<void> => {
    if (!req.session.idtoken) {
      res.status(400).send('No idToken submitted. Have you enabled cookies?')
{'fraction_non_alphanumeric': 0.15917011103006726, 'fraction_numerical': 0.021395575006078287, 'mean_word_length': 2.844236760124611, 'pattern_counts': {'":': 0, '<': 15, '<?xml version=': 0, '>': 33, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 22, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5898919', 'n_tokens_mistral': 4239, 'n_tokens_neox': 4004, 'n_words': 940}