app.js 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134
  1. import { escape } from 'html-escaper'
  2. import { Server } from 'hyper-express'
  3. import { LRUCache } from 'lru-cache'
  4. import cluster, { isWorker } from 'node:cluster'
  5. import { maxQuery, maxRows } from './config.js'
  6. const { DATABASE } = process.env
  7. const db = DATABASE ? await import(`./database/${DATABASE}.js`) : null
  8. const generateRandomNumber = () => Math.ceil(Math.random() * maxRows)
  9. const parseQueries = (i) => Math.min(Math.max(parseInt(i, 10) || 1, 1), maxQuery)
  10. const cache = new LRUCache({
  11. max: maxRows
  12. })
  13. const app = new Server()
  14. // use middleware to add `Server` into response header
  15. app.use((_request, response, next) => {
  16. response.header('Server', 'hyperexpress')
  17. next()
  18. })
  19. app.get('/plaintext', (_request, response) => {
  20. response.atomic(() => {
  21. response
  22. .type('text')
  23. .send('Hello, World!')
  24. })
  25. })
  26. app.get('/json', (_request, response) => {
  27. response.json({ message: 'Hello, World!' })
  28. })
  29. if (db) {
  30. // populate cache
  31. (async () => {
  32. const worlds = await db.getAllWorlds()
  33. for (let i = 0; i < worlds.length; i++) {
  34. cache.set(worlds[i].id, worlds[i])
  35. }
  36. })()
  37. app.get('/db', async (_request, response) => {
  38. try {
  39. const world = await db.find(generateRandomNumber())
  40. response.json(world)
  41. } catch (error) {
  42. throw error
  43. }
  44. })
  45. app.get('/queries', async (request, response) => {
  46. try {
  47. const queries = parseQueries(request.query.queries)
  48. const worldPromises = []
  49. for (let i = 0; i < queries; i++) {
  50. worldPromises.push(db.find(generateRandomNumber()))
  51. }
  52. const worlds = await Promise.all(worldPromises)
  53. response.json(worlds)
  54. } catch (error) {
  55. throw error
  56. }
  57. })
  58. app.get('/updates', async (request, response) => {
  59. try {
  60. const queries = parseQueries(request.query.queries)
  61. const worldPromises = []
  62. for (let i = 0; i < queries; i++) {
  63. worldPromises.push(db.find(generateRandomNumber()))
  64. }
  65. const worlds = await Promise.all(worldPromises)
  66. const updatedWorlds = await Promise.all(worlds.map(async (world) => {
  67. world.randomNumber = generateRandomNumber()
  68. await db.update(world)
  69. return world
  70. }))
  71. response.json(updatedWorlds)
  72. } catch (error) {
  73. throw error
  74. }
  75. })
  76. app.get('/fortunes', async (_request, response) => {
  77. try {
  78. const fortunes = await db.fortunes()
  79. fortunes.push({ id: 0, message: 'Additional fortune added at request time.' })
  80. fortunes.sort((a, b) => a.message.localeCompare(b.message))
  81. let i = 0, html = '<!DOCTYPE html><html><head><title>Fortunes</title></head><body><table><tr><th>id</th><th>message</th></tr>'
  82. for (; i < fortunes.length; i++) html += `<tr><td>${fortunes[i].id}</td><td>${escape(fortunes[i].message)}</td></tr>`
  83. html += '</table></body></html>'
  84. response.atomic(() => {
  85. response
  86. // .type('html')
  87. .header('Content-Type', 'text/html; charset=utf-8')
  88. .send(html)
  89. })
  90. } catch (error) {
  91. throw error
  92. }
  93. })
  94. app.get('/cached-worlds', async (request, response) => {
  95. try {
  96. const count = parseQueries(request.query.count)
  97. const worlds = []
  98. for (let i = 0; i < count; i++) {
  99. worlds[i] = cache.get(generateRandomNumber())
  100. }
  101. response.json(worlds)
  102. } catch (error) {
  103. throw error
  104. }
  105. })
  106. }
  107. app.listen(8080).then(() => {
  108. console.log(`${isWorker ? `${cluster.worker.id}: ` : ''}Successfully bound to http://0.0.0.0:8080`)
  109. })