link-shortner/utils/helperFunctions.js

145 lines
4.4 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

const moment = require("moment");
const db = require("../dbConfig");
const crypto = require("crypto");
const cron = require("node-cron");
/**
* Checks if a given string is a valid URL format.
* @param {string} url - The input string to validate.
* @returns {boolean} - Returns true if valid URL format, otherwise false.
*/
function isValidUrl(url) {
try {
new URL(url);
return true;
} catch (e) {
return false;
}
}
/**
* Generates a deterministic short code based on long URL, expiry date, and user ID.
* Uses MD5 hashing, encodes the result in base64, and slices to desired length.
* @param {string} longUrl - Original input URL.
* @param {number} codeLength - Desired length of output short code.
* @param {string} expiryDate - Expiry date used in code generation.
* @param {string} user_id - User identifier to namespace the code.
* @returns {string} - Base64-based truncated hash used as short code.
*/
const generateShortCode = (
longUrl,
codeLength,
expiryDate,
userId,
customizedShortnerSlug
) => {
const dataToHash =
longUrl + "_" + expiryDate + "_" + userId + "_" + customizedShortnerSlug;
return crypto
.createHash("md5")
.update(dataToHash)
.digest("base64")
.substring(0, codeLength);
};
/**
* Creates and stores a new short code mapping for a given URL.
* Validates input values and optionally supports user-scoped or custom slugs.
* @param {object} params - Input parameters for short code creation.
* @param {string} params.longUrl - Original full URL.
* @param {number} params.expiryInDays - Days until expiry (030 allowed).
* @param {number} params.codeLength - Short code length (830 allowed).
* @param {string} [params.user_id=""] - Optional user ID for namespace isolation.
* @param {string} [params.customized_shortner_slug=""] - Optional custom alias.
* @returns {Promise<string>} - Returns existing or newly created code/slug.
* @throws {Error} - If input validation fails or database errors occur.
*/
const createShortUrl = async ({
longUrl = "",
expiryInDays = 30,
codeLength = 10,
userId = "",
customizedShortnerSlug = "",
}) => {
if (!String(longUrl).trim() || !isValidUrl(longUrl)) {
throw new Error("Long url required.");
}
if (!Number.isInteger(codeLength)) {
throw new Error("Code length must be valid integer value.");
} else if (codeLength < 8 || codeLength > 30) {
throw new Error("Code length must be between 8 to 30 characters.");
}
if (!Number.isInteger(expiryInDays)) {
throw new Error("Expiry in days must be valid integer value.");
} else if (expiryInDays < 0 || expiryInDays > 30) {
throw new Error("Expiry in days must be between 0 to 30 days.");
}
const expiryDate = moment()
.add(expiryInDays, "days")
.endOf("day")
.format("YYYY-MM-DD HH:mm:ss");
// Avoid duplicates: return existing code if it already exists
const existing = await db.findByLongUrl(
longUrl,
expiryDate,
userId,
customizedShortnerSlug
);
if (existing) return existing.customized_shortner_slug || existing.short_code;
const shortCode = generateShortCode(
longUrl,
codeLength,
expiryDate,
userId,
customizedShortnerSlug
);
const res = await db.insertUrl(
longUrl,
shortCode,
expiryDate,
String(userId).trim(),
String(customizedShortnerSlug).trim()
);
return customizedShortnerSlug || shortCode;
};
/**
* Retrieves the original long URL using a short code or custom slug.
* @param {string} shortCode - Code or slug used to look up the original URL.
* @param {string} [userId=""] - Optional user ID for user-specific resolution.
* @returns {Promise<string>} - Returns the original URL if found and valid.
* @throws {Error} - If lookup fails or URL is expired.
*/
const getOriginalUrl = async (shortCode, userId = "") => {
try {
const longUrl = await db.findByShortCode(shortCode, userId);
return longUrl;
} catch (error) {
throw error;
}
};
/**
* Starts a cron job that runs daily at midnight.
* Cleans up expired short URL entries from the database.
*/
const startScheduler = () => {
console.log("Starting expiration cleanup scheduler...");
cron.schedule("0 0 * * *", () => {
console.log("[Scheduler] Running deletion job...");
db.deleteAllExpired().then(console.log).catch(console.error);
});
};
module.exports = {
createShortUrl,
getOriginalUrl,
startScheduler,
getAllData: db.getAllData,
getPaginatedData: db.getPaginatedData,
};