index.ts 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117
  1. import ms from "ms";
  2. import {
  3. differenceInDays,
  4. differenceInHours,
  5. differenceInMonths
  6. } from "date-fns";
  7. import generate from "nanoid/generate";
  8. import { findLinkQuery } from "../queries/link";
  9. export class CustomError extends Error {
  10. public statusCode?: number;
  11. public data?: any;
  12. public constructor(message: string, statusCode = 500, data?: any) {
  13. super(message);
  14. this.name = this.constructor.name;
  15. this.statusCode = statusCode;
  16. this.data = data;
  17. }
  18. }
  19. export const generateId = async (domainId: number = null) => {
  20. const address = generate(
  21. "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890",
  22. Number(process.env.LINK_LENGTH) || 6
  23. );
  24. const link = await findLinkQuery({ address, domainId });
  25. if (!link) return address;
  26. return generateId(domainId);
  27. };
  28. export const addProtocol = (url: string): string => {
  29. const hasProtocol = /^\w+:\/\//.test(url);
  30. return hasProtocol ? url : `http://${url}`;
  31. };
  32. export const generateShortLink = (id: string, domain?: string): string => {
  33. const protocol =
  34. process.env.CUSTOM_DOMAIN_USE_HTTPS === "true" || !domain
  35. ? "https://"
  36. : "http://";
  37. return `${protocol}${domain || process.env.DEFAULT_DOMAIN}/${id}`;
  38. };
  39. export const isAdmin = (email: string): boolean =>
  40. process.env.ADMIN_EMAILS.split(",")
  41. .map(e => e.trim())
  42. .includes(email);
  43. export const getRedisKey = {
  44. link: (address: string, domain_id?: number, user_id?: number) =>
  45. `${address}-${domain_id || ""}-${user_id || ""}`,
  46. domain: (address: string) => `d-${address}`,
  47. host: (address: string) => `h-${address}`,
  48. user: (emailOrKey: string) => `u-${emailOrKey}`
  49. };
  50. // TODO: Add statsLimit
  51. export const getStatsLimit = (): number =>
  52. Number(process.env.DEFAULT_MAX_STATS_PER_LINK) || 100000000;
  53. export const getStatsCacheTime = (total?: number): number => {
  54. let durationInMs;
  55. switch (true) {
  56. case total <= 5000:
  57. durationInMs = ms("5 minutes");
  58. break;
  59. case total > 5000 && total < 20000:
  60. durationInMs = ms("10 minutes");
  61. break;
  62. case total < 40000:
  63. durationInMs = ms("15 minutes");
  64. break;
  65. case total > 40000:
  66. durationInMs = ms("30 minutes");
  67. break;
  68. default:
  69. durationInMs = ms("5 minutes");
  70. }
  71. return durationInMs / 1000;
  72. };
  73. export const statsObjectToArray = (obj: Stats) => {
  74. const objToArr = key =>
  75. Array.from(Object.keys(obj[key]))
  76. .map(name => ({
  77. name,
  78. value: obj[key][name]
  79. }))
  80. .sort((a, b) => b.value - a.value);
  81. return {
  82. browser: objToArr("browser"),
  83. os: objToArr("os"),
  84. country: objToArr("country"),
  85. referrer: objToArr("referrer")
  86. };
  87. };
  88. export const getDifferenceFunction = (
  89. type: "lastDay" | "lastWeek" | "lastMonth" | "allTime"
  90. ): Function => {
  91. if (type === "lastDay") return differenceInHours;
  92. if (type === "lastWeek") return differenceInDays;
  93. if (type === "lastMonth") return differenceInDays;
  94. if (type === "allTime") return differenceInMonths;
  95. throw new Error("Unknown type.");
  96. };
  97. export const getUTCDate = (dateString?: Date) => {
  98. const date = new Date(dateString || Date.now());
  99. return new Date(
  100. date.getUTCFullYear(),
  101. date.getUTCMonth(),
  102. date.getUTCDate(),
  103. date.getUTCHours()
  104. );
  105. };