kutt/server/queries/link.queries.js

311 lines
7.4 KiB
JavaScript
Raw Permalink Normal View History

2024-08-11 18:41:03 +03:30
const bcrypt = require("bcryptjs");
2020-01-11 17:40:25 +03:30
2024-10-07 09:08:40 +03:30
const utils = require("../utils");
2024-08-11 18:41:03 +03:30
const redis = require("../redis");
const knex = require("../knex");
2024-10-21 14:59:55 +03:30
const env = require("../env");
2020-01-11 17:40:25 +03:30
2024-10-07 09:08:40 +03:30
const CustomError = utils.CustomError;
2020-01-30 18:51:52 +03:30
const selectable = [
"links.id",
"links.address",
"links.banned",
"links.created_at",
"links.domain_id",
"links.updated_at",
"links.password",
"links.description",
2020-08-04 20:32:23 +04:30
"links.expire_in",
2020-01-30 18:51:52 +03:30
"links.target",
"links.visit_count",
"links.user_id",
"links.uuid",
"domains.address as domain"
];
2024-11-19 07:58:57 +03:30
const selectable_admin = [
...selectable,
"users.email as email"
];
2024-08-11 18:41:03 +03:30
function normalizeMatch(match) {
2020-01-30 18:51:52 +03:30
const newMatch = { ...match };
if (newMatch.address) {
newMatch["links.address"] = newMatch.address;
delete newMatch.address;
}
if (newMatch.user_id) {
newMatch["links.user_id"] = newMatch.user_id;
delete newMatch.user_id;
}
if (newMatch.id) {
newMatch["links.id"] = newMatch.id;
delete newMatch.id;
}
2020-01-30 18:51:52 +03:30
if (newMatch.uuid) {
newMatch["links.uuid"] = newMatch.uuid;
delete newMatch.uuid;
}
2024-11-19 07:58:57 +03:30
if (newMatch.banned !== undefined) {
newMatch["links.banned"] = newMatch.banned;
delete newMatch.banned;
}
2020-01-30 18:51:52 +03:30
return newMatch;
2024-09-14 00:17:21 +02:00
}
2020-01-30 18:51:52 +03:30
2024-08-11 18:41:03 +03:30
async function total(match, params) {
const normalizedMatch = normalizeMatch(match);
const query = knex("links");
2024-08-11 18:41:03 +03:30
Object.entries(normalizedMatch).forEach(([key, value]) => {
query.andWhere(key, ...(Array.isArray(value) ? value : [value]));
});
2024-08-11 18:41:03 +03:30
if (params?.search) {
query[knex.compatibleILIKE](
knex.raw("concat_ws(' ', description, links.address, target, domains.address)"),
"%" + params.search + "%"
);
2020-01-11 17:40:25 +03:30
}
2024-08-21 21:22:59 +03:30
query.leftJoin("domains", "links.domain_id", "domains.id");
query.count("* as count");
2024-08-11 18:41:03 +03:30
2024-08-21 21:22:59 +03:30
const [{ count }] = await query;
2020-01-11 17:40:25 +03:30
return typeof count === "number" ? count : parseInt(count);
}
2024-11-19 07:58:57 +03:30
async function totalAdmin(match, params) {
const query = knex("links");
Object.entries(normalizeMatch(match)).forEach(([key, value]) => {
query.andWhere(key, ...(Array.isArray(value) ? value : [value]));
});
if (params?.user) {
const id = parseInt(params?.user);
if (Number.isNaN(id)) {
query[knex.compatibleILIKE]("users.email", "%" + params.user + "%");
2024-11-19 07:58:57 +03:30
} else {
query.andWhere("links.user_id", params.user);
}
}
if (params?.search) {
query[knex.compatibleILIKE](
knex.raw("concat_ws(' ', description, links.address, target)"),
"%" + params.search + "%"
2024-11-19 07:58:57 +03:30
);
}
if (params?.domain) {
query[knex.compatibleILIKE]("domains.address", "%" + params.domain + "%");
2024-11-19 07:58:57 +03:30
}
query.leftJoin("domains", "links.domain_id", "domains.id");
query.leftJoin("users", "links.user_id", "users.id");
query.count("* as count");
2024-11-19 07:58:57 +03:30
const [{ count }] = await query;
return typeof count === "number" ? count : parseInt(count);
}
2024-08-11 18:41:03 +03:30
async function get(match, params) {
const query = knex("links")
2020-01-30 18:51:52 +03:30
.select(...selectable)
.where(normalizeMatch(match))
.offset(params.skip)
.limit(params.limit)
2024-11-19 07:58:57 +03:30
.orderBy("links.id", "desc");
2024-08-11 18:41:03 +03:30
if (params?.search) {
query[knex.compatibleILIKE](
knex.raw("concat_ws(' ', description, links.address, target, domains.address)"),
"%" + params.search + "%"
);
2020-01-11 17:40:25 +03:30
}
2024-08-11 18:41:03 +03:30
2020-01-11 17:40:25 +03:30
query.leftJoin("domains", "links.domain_id", "domains.id");
2024-09-14 04:50:29 +02:00
return query;
2024-08-11 18:41:03 +03:30
}
2020-01-11 17:40:25 +03:30
2024-11-19 07:58:57 +03:30
async function getAdmin(match, params) {
const query = knex("links").select(...selectable_admin);
Object.entries(normalizeMatch(match)).forEach(([key, value]) => {
query.andWhere(key, ...(Array.isArray(value) ? value : [value]));
});
query
.orderBy("links.id", "desc")
.offset(params.skip)
.limit(params.limit)
if (params?.user) {
const id = parseInt(params?.user);
if (Number.isNaN(id)) {
query[knex.compatibleILIKE]("users.email", "%" + params.user + "%");
2024-11-19 07:58:57 +03:30
} else {
query.andWhere("links.user_id", params.user);
}
}
if (params?.search) {
query[knex.compatibleILIKE](
knex.raw("concat_ws(' ', description, links.address, target)"),
"%" + params.search + "%"
2024-11-19 07:58:57 +03:30
);
}
if (params?.domain) {
query[knex.compatibleILIKE]("domains.address", "%" + params.domain + "%");
2024-11-19 07:58:57 +03:30
}
query.leftJoin("domains", "links.domain_id", "domains.id");
query.leftJoin("users", "links.user_id", "users.id");
return query;
}
2024-08-11 18:41:03 +03:30
async function find(match) {
2025-01-07 22:01:20 +03:30
if (match.address && match.domain_id !== undefined && env.REDIS_ENABLED) {
2020-01-30 18:51:52 +03:30
const key = redis.key.link(match.address, match.domain_id);
2024-08-11 18:41:03 +03:30
const cachedLink = await redis.client.get(key);
2020-01-30 18:51:52 +03:30
if (cachedLink) return JSON.parse(cachedLink);
}
2024-08-11 18:41:03 +03:30
const link = await knex("links")
2020-01-30 18:51:52 +03:30
.select(...selectable)
.where(normalizeMatch(match))
.leftJoin("domains", "links.domain_id", "domains.id")
2020-01-11 17:40:25 +03:30
.first();
2024-08-11 18:41:03 +03:30
2024-10-21 14:59:55 +03:30
if (link && env.REDIS_ENABLED) {
2020-01-30 18:51:52 +03:30
const key = redis.key.link(link.address, link.domain_id);
2024-10-21 14:59:55 +03:30
redis.client.set(key, JSON.stringify(link), "EX", 60 * 15);
2020-01-11 17:40:25 +03:30
}
2024-08-11 18:41:03 +03:30
2020-01-11 17:40:25 +03:30
return link;
}
2024-08-11 18:41:03 +03:30
async function create(params) {
let encryptedPassword = null;
2020-01-30 18:51:52 +03:30
if (params.password) {
2020-01-11 17:40:25 +03:30
const salt = await bcrypt.genSalt(12);
2020-01-30 18:51:52 +03:30
encryptedPassword = await bcrypt.hash(params.password, salt);
2020-01-11 17:40:25 +03:30
}
2024-08-11 18:41:03 +03:30
let [link] = await knex(
2020-01-30 18:51:52 +03:30
"links"
).insert(
2020-01-11 17:40:25 +03:30
{
password: encryptedPassword,
2020-01-30 18:51:52 +03:30
domain_id: params.domain_id || null,
user_id: params.user_id || null,
address: params.address,
description: params.description || null,
2020-08-04 20:32:23 +04:30
expire_in: params.expire_in || null,
2020-01-30 18:51:52 +03:30
target: params.target
2020-01-11 17:40:25 +03:30
},
"*"
);
// mysql doesn't return the whole link, but rather the id number only
// so we need to fetch the link ourselves
if (typeof link === "number") {
link = await knex("links").where("id", link).first();
}
2020-01-30 18:51:52 +03:30
return link;
2024-08-11 18:41:03 +03:30
}
2020-01-30 18:51:52 +03:30
2024-08-11 18:41:03 +03:30
async function remove(match) {
const link = await knex("links").where(match).first();
2020-01-30 18:51:52 +03:30
if (!link) {
2024-08-21 21:22:59 +03:30
return { isRemoved: false, error: "Could not find the link.", link: null }
2024-09-14 00:17:21 +02:00
}
2024-08-21 21:22:59 +03:30
2024-08-11 18:41:03 +03:30
const deletedLink = await knex("links").where("id", link.id).delete();
2024-10-21 14:59:55 +03:30
if (env.REDIS_ENABLED) {
redis.remove.link(link);
}
2024-08-11 18:41:03 +03:30
2024-08-21 21:22:59 +03:30
return { isRemoved: !!deletedLink, link };
2024-08-11 18:41:03 +03:30
}
2020-08-04 20:32:23 +04:30
2024-08-11 18:41:03 +03:30
async function batchRemove(match) {
2024-11-19 07:58:57 +03:30
const query = knex("links");
2024-08-11 18:41:03 +03:30
2020-08-04 20:32:23 +04:30
Object.entries(match).forEach(([key, value]) => {
2024-11-19 07:58:57 +03:30
query.andWhere(key, ...(Array.isArray(value) ? value : [value]));
2020-08-04 20:32:23 +04:30
});
2024-08-11 18:41:03 +03:30
2024-11-19 07:58:57 +03:30
const links = await query.clone();
2024-08-11 18:41:03 +03:30
2024-11-19 07:58:57 +03:30
await query.delete();
2024-10-07 09:08:40 +03:30
2024-10-21 14:59:55 +03:30
if (env.REDIS_ENABLED) {
links.forEach(redis.remove.link);
}
2024-08-11 18:41:03 +03:30
}
2020-08-04 20:32:23 +04:30
2024-08-11 18:41:03 +03:30
async function update(match, update) {
2021-12-03 12:56:07 +11:00
if (update.password) {
const salt = await bcrypt.genSalt(12);
update.password = await bcrypt.hash(update.password, salt);
}
// if the links' adddress or domain is changed,
// make sure to delete the original links from cache
let links = []
if (env.REDIS_ENABLED && (update.address || update.domain_id)) {
links = await knex("links").select('*').where(match);
}
2024-08-11 18:41:03 +03:30
2024-09-14 00:17:21 +02:00
await knex("links")
2020-01-30 18:51:52 +03:30
.where(match)
2024-10-07 09:08:40 +03:30
.update({ ...update, updated_at: utils.dateToUTC(new Date()) });
2024-09-14 00:17:21 +02:00
const updated_links = await knex("links")
.select(selectable)
.where(normalizeMatch(match))
.leftJoin("domains", "links.domain_id", "domains.id");
2024-10-21 14:59:55 +03:30
if (env.REDIS_ENABLED) {
links.forEach(redis.remove.link);
updated_links.forEach(redis.remove.link);
2024-10-21 14:59:55 +03:30
}
2024-08-11 18:41:03 +03:30
return updated_links;
2024-08-11 18:41:03 +03:30
}
2020-01-30 18:51:52 +03:30
2024-08-11 18:41:03 +03:30
function incrementVisit(match) {
return knex("links").where(match).increment("visit_count", 1);
}
module.exports = {
normalizeMatch,
batchRemove,
create,
find,
get,
2024-11-19 07:58:57 +03:30
getAdmin,
2024-08-11 18:41:03 +03:30
incrementVisit,
remove,
total,
2024-11-19 07:58:57 +03:30
totalAdmin,
2024-08-11 18:41:03 +03:30
update,
}