You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

148 lines
3.9 KiB

  1. const AbstractClientStore = require('express-brute/lib/AbstractClientStore')
  2. const KnexStore = module.exports = function (options) {
  3. options = options || Object.create(null)
  4. AbstractClientStore.apply(this, arguments)
  5. this.options = Object.assign(Object.create(null), KnexStore.defaults, options)
  6. if (this.options.knex) {
  7. this.knex = this.options.knex
  8. } else {
  9. this.knex = require('knex')(KnexStore.defaultsKnex)
  10. }
  11. if (options.createTable === false) {
  12. this.ready = Promise.resolve()
  13. } else {
  14. this.ready = this.knex.schema.hasTable(this.options.tablename)
  15. .then((exists) => {
  16. if (exists) {
  17. return
  18. }
  19. return this.knex.schema.createTable(this.options.tablename, (table) => {
  20. table.string('key')
  21. table.bigInteger('firstRequest').nullable()
  22. table.bigInteger('lastRequest').nullable()
  23. table.bigInteger('lifetime').nullable()
  24. table.integer('count')
  25. })
  26. })
  27. }
  28. }
  29. KnexStore.prototype = Object.create(AbstractClientStore.prototype)
  30. KnexStore.prototype.set = async function (key, value, lifetime, callback) {
  31. try {
  32. lifetime = lifetime || 0
  33. await this.ready
  34. const resp = await this.knex.transaction((trx) => {
  35. return trx
  36. .select('*')
  37. .forUpdate()
  38. .from(this.options.tablename)
  39. .where('key', '=', key)
  40. .then((foundKeys) => {
  41. if (foundKeys.length === 0) {
  42. return trx.from(this.options.tablename)
  43. .insert({
  44. key: key,
  45. lifetime: new Date(Date.now() + lifetime * 1000).getTime(),
  46. lastRequest: new Date(value.lastRequest).getTime(),
  47. firstRequest: new Date(value.firstRequest).getTime(),
  48. count: value.count
  49. })
  50. } else {
  51. return trx(this.options.tablename)
  52. .where('key', '=', key)
  53. .update({
  54. lifetime: new Date(Date.now() + lifetime * 1000).getTime(),
  55. count: value.count,
  56. lastRequest: new Date(value.lastRequest).getTime()
  57. })
  58. }
  59. })
  60. })
  61. callback(null, resp)
  62. } catch (err) {
  63. callback(err, null)
  64. }
  65. }
  66. KnexStore.prototype.get = async function (key, callback) {
  67. try {
  68. await this.ready
  69. await this.clearExpired()
  70. const resp = await this.knex.select('*')
  71. .from(this.options.tablename)
  72. .where('key', '=', key)
  73. let o = null
  74. if (resp[0]) {
  75. o = {}
  76. o.lastRequest = new Date(resp[0].lastRequest)
  77. o.firstRequest = new Date(resp[0].firstRequest)
  78. o.count = resp[0].count
  79. }
  80. callback(null, o)
  81. } catch (err) {
  82. callback(err, null)
  83. }
  84. }
  85. KnexStore.prototype.reset = async function (key, callback) {
  86. try {
  87. await this.ready
  88. const resp = await this.knex(this.options.tablename)
  89. .where('key', '=', key)
  90. .del()
  91. callback(null, resp)
  92. } catch (err) {
  93. callback(err, null)
  94. }
  95. }
  96. KnexStore.prototype.increment = async function (key, lifetime, callback) {
  97. try {
  98. const result = await this.get(key)
  99. let resp = null
  100. if (result) {
  101. resp = await this.knex(this.options.tablename)
  102. .increment('count', 1)
  103. .where('key', '=', key)
  104. } else {
  105. resp = await this.knex(this.options.tablename)
  106. .insert({
  107. key: key,
  108. firstRequest: new Date().getTime(),
  109. lastRequest: new Date().getTime(),
  110. lifetime: new Date(Date.now() + lifetime * 1000).getTime(),
  111. count: 1
  112. })
  113. }
  114. callback(null, resp)
  115. } catch (err) {
  116. callback(err, null)
  117. }
  118. }
  119. KnexStore.prototype.clearExpired = async function (callback) {
  120. await this.ready
  121. return this.knex(this.options.tablename)
  122. .del()
  123. .where('lifetime', '<', new Date().getTime())
  124. }
  125. KnexStore.defaults = {
  126. tablename: 'brute',
  127. createTable: true
  128. }
  129. KnexStore.defaultsKnex = {
  130. client: 'sqlite3',
  131. // debug: true,
  132. connection: {
  133. filename: './brute-knex.sqlite'
  134. }
  135. }