link.queries.js 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195
  1. const bcrypt = require("bcryptjs");
  2. const CustomError = require("../utils").CustomError;
  3. const redis = require("../redis");
  4. const knex = require("../knex");
  5. const selectable = [
  6. "links.id",
  7. "links.address",
  8. "links.banned",
  9. "links.created_at",
  10. "links.domain_id",
  11. "links.updated_at",
  12. "links.password",
  13. "links.description",
  14. "links.expire_in",
  15. "links.target",
  16. "links.visit_count",
  17. "links.user_id",
  18. "links.uuid",
  19. "domains.address as domain"
  20. ];
  21. function normalizeMatch(match) {
  22. const newMatch = { ...match };
  23. if (newMatch.address) {
  24. newMatch["links.address"] = newMatch.address;
  25. delete newMatch.address;
  26. }
  27. if (newMatch.user_id) {
  28. newMatch["links.user_id"] = newMatch.user_id;
  29. delete newMatch.user_id;
  30. }
  31. if (newMatch.uuid) {
  32. newMatch["links.uuid"] = newMatch.uuid;
  33. delete newMatch.uuid;
  34. }
  35. return newMatch;
  36. }
  37. async function total(match, params) {
  38. let query = knex("links");
  39. Object.entries(normalizeMatch(match)).forEach(([key, value]) => {
  40. query.andWhere(key, ...(Array.isArray(value) ? value : [value]));
  41. });
  42. if (params?.search) {
  43. query.andWhereRaw(
  44. "concat_ws(' ', description, links.address, target, domains.address) ILIKE '%' || ? || '%'",
  45. [params.search]
  46. );
  47. }
  48. query.leftJoin("domains", "links.domain_id", "domains.id");
  49. query.count("links.id");
  50. const [{ count }] = await query;
  51. return typeof count === "number" ? count : parseInt(count);
  52. }
  53. async function get(match, params) {
  54. const query = knex("links")
  55. .select(...selectable)
  56. .where(normalizeMatch(match))
  57. .offset(params.skip)
  58. .limit(params.limit)
  59. .orderBy("links.created_at", "desc");
  60. if (params?.search) {
  61. query.andWhereRaw(
  62. "concat_ws(' ', description, links.address, target, domains.address) ILIKE '%' || ? || '%'",
  63. [params.search]
  64. );
  65. }
  66. query.leftJoin("domains", "links.domain_id", "domains.id");
  67. return query;
  68. }
  69. async function find(match) {
  70. if (match.address && match.domain_id) {
  71. const key = redis.key.link(match.address, match.domain_id);
  72. const cachedLink = await redis.client.get(key);
  73. if (cachedLink) return JSON.parse(cachedLink);
  74. }
  75. const link = await knex("links")
  76. .select(...selectable)
  77. .where(normalizeMatch(match))
  78. .leftJoin("domains", "links.domain_id", "domains.id")
  79. .first();
  80. if (link) {
  81. const key = redis.key.link(link.address, link.domain_id);
  82. redis.client.set(key, JSON.stringify(link), "EX", 60 * 60 * 2);
  83. }
  84. return link;
  85. }
  86. async function create(params) {
  87. let encryptedPassword = null;
  88. if (params.password) {
  89. const salt = await bcrypt.genSalt(12);
  90. encryptedPassword = await bcrypt.hash(params.password, salt);
  91. }
  92. const [link] = await knex(
  93. "links"
  94. ).insert(
  95. {
  96. password: encryptedPassword,
  97. domain_id: params.domain_id || null,
  98. user_id: params.user_id || null,
  99. address: params.address,
  100. description: params.description || null,
  101. expire_in: params.expire_in || null,
  102. target: params.target
  103. },
  104. "*"
  105. );
  106. return link;
  107. }
  108. async function remove(match) {
  109. const link = await knex("links").where(match).first();
  110. if (!link) {
  111. return { isRemoved: false, error: "Could not find the link.", link: null }
  112. }
  113. const deletedLink = await knex("links").where("id", link.id).delete();
  114. redis.remove.link(link);
  115. return { isRemoved: !!deletedLink, link };
  116. }
  117. async function batchRemove(match) {
  118. const deleteQuery = knex("links");
  119. const findQuery = knex("links");
  120. Object.entries(match).forEach(([key, value]) => {
  121. findQuery.andWhere(key, ...(Array.isArray(value) ? value : [value]));
  122. deleteQuery.andWhere(key, ...(Array.isArray(value) ? value : [value]));
  123. });
  124. const links = await findQuery;
  125. links.forEach(redis.remove.link);
  126. await deleteQuery.delete();
  127. }
  128. async function update(match, update) {
  129. if (update.password) {
  130. const salt = await bcrypt.genSalt(12);
  131. update.password = await bcrypt.hash(update.password, salt);
  132. }
  133. await knex("links")
  134. .where(match)
  135. .update({ ...update, updated_at: new Date().toISOString() });
  136. const links = await knex("links")
  137. .select('*')
  138. .where(match);
  139. links.forEach(redis.remove.link);
  140. return links;
  141. }
  142. function incrementVisit(match) {
  143. return knex("links").where(match).increment("visit_count", 1);
  144. }
  145. module.exports = {
  146. normalizeMatch,
  147. batchRemove,
  148. create,
  149. find,
  150. get,
  151. incrementVisit,
  152. remove,
  153. total,
  154. update,
  155. }