link.queries.js 4.6 KB

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