link.queries.js 4.5 KB

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