| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266 |
- const { addMinutes } = require("date-fns");
- const { randomUUID } = require("node:crypto");
- const { ROLES } = require("../consts");
- const utils = require("../utils");
- const redis = require("../redis");
- const knex = require("../knex");
- const env = require("../env");
- async function find(match) {
- if ((match.id || match.apikey) && env.REDIS_ENABLED) {
- const key = redis.key.user(match.id || match.apikey);
- const cachedUser = await redis.client.get(key);
- if (cachedUser) return JSON.parse(cachedUser);
- }
- const query = knex("users");
- Object.entries(match).forEach(([key, value]) => {
- query.andWhere(key, ...(Array.isArray(value) ? value : [value]));
- });
- const user = await query.first();
-
- if (user && env.REDIS_ENABLED) {
- if (match.id) {
- const idKey = redis.key.user(user.id);
- redis.client.set(idKey, JSON.stringify(user), "EX", 60 * 15);
- }
-
- if (match.apikey) {
- const apikeyKey = redis.key.user(user.apikey);
- redis.client.set(apikeyKey, JSON.stringify(user), "EX", 60 * 15);
- }
- }
-
- return user;
- }
- async function add(params, user) {
- const data = {
- email: params.email,
- password: params.password,
- ...(params.role && { role: params.role }),
- ...(params.verified !== undefined && { verified: params.verified }),
- verification_token: randomUUID(),
- verification_expires: utils.dateToUTC(addMinutes(new Date(), 60))
- };
-
- if (user) {
- await knex("users")
- .where("id", user.id)
- .update({ ...data, updated_at: utils.dateToUTC(new Date()) });
- } else {
- await knex("users").insert(data);
- }
-
- if (env.REDIS_ENABLED) {
- redis.remove.user(user);
- }
-
- return {
- ...user,
- ...data
- };
- }
- async function update(match, update, methods) {
- const { user, updated_user } = await knex.transaction(async function(trx) {
- const query = trx("users");
- Object.entries(match).forEach(([key, value]) => {
- query.andWhere(key, ...(Array.isArray(value) ? value : [value]));
- });
- const user = await query.select("id").first();
- if (!user) return null;
-
- const updateQuery = trx("users").where("id", user.id);
- if (methods?.increments) {
- methods.increments.forEach(columnName => {
- updateQuery.increment(columnName);
- });
- }
-
- await updateQuery.update({ ...update, updated_at: utils.dateToUTC(new Date()) });
- const updated_user = await trx("users").where("id", user.id).first();
- return { user, updated_user };
- });
- if (env.REDIS_ENABLED && user) {
- redis.remove.user(user);
- redis.remove.user(updated_user);
- }
- return updated_user;
- }
- async function remove(user) {
- const deletedUser = await knex("users").where("id", user.id).delete();
-
- if (env.REDIS_ENABLED) {
- redis.remove.user(user);
- }
-
- return !!deletedUser;
- }
- const selectable_admin = [
- "users.id",
- "users.email",
- "users.verified",
- "users.role",
- "users.banned",
- "users.banned_by_id",
- "users.created_at",
- "users.updated_at"
- ];
- function normalizeMatch(match) {
- const newMatch = { ...match }
- if (newMatch.banned !== undefined) {
- newMatch["users.banned"] = newMatch.banned;
- delete newMatch.banned;
- }
- return newMatch;
- }
- async function getAdmin(match, params) {
- const query = knex("users")
- .select(...selectable_admin)
- .select("l.links_count")
- .select("d.domains")
- .fromRaw("users")
- .where(normalizeMatch(match))
- .offset(params.skip)
- .limit(params.limit)
- .orderBy("users.id", "desc")
- .groupBy(1)
- .groupBy("l.links_count")
- .groupBy("d.domains");
-
- if (params?.search) {
- const id = parseInt(params?.search);
- if (Number.isNaN(id)) {
- query[knex.compatibleILIKE]("users.email", "%" + params?.search + "%");
- } else {
- query.andWhere("users.id", params?.search);
- }
- }
- if (params?.domains !== undefined) {
- query.andWhere("d.domains", params?.domains ? "is not" : "is", null);
- }
- if (params?.links !== undefined) {
- query.andWhere("links_count", params?.links ? "is not" : "is", null);
- }
-
- query.leftJoin(
- knex("domains")
- .select("user_id", knex.isMySQL
- ? knex.raw("group_concat(address SEPARATOR ', ') AS domains")
- : knex.raw("string_agg(address, ', ') AS domains")
- )
- .groupBy("user_id").as("d"),
- "users.id",
- "d.user_id"
- )
- query.leftJoin(
- knex("links").select("user_id").count("* as links_count").groupBy("user_id").as("l"),
- "users.id",
- "l.user_id"
- );
- return query;
- }
- async function totalAdmin(match, params) {
- const query = knex("users")
- .count("* as count")
- .fromRaw("users")
- .where(normalizeMatch(match));
- if (params?.search) {
- const id = parseInt(params?.search);
- if (Number.isNaN(id)) {
- query[knex.compatibleILIKE]("users.email", "%" + params?.search + "%");
- } else {
- query.andWhere("users.id", params?.search);
- }
- }
- if (params?.domains !== undefined) {
- query.andWhere("domains", params?.domains ? "is not" : "is", null);
- query.leftJoin(
- knex("domains")
- .select("user_id", knex.isMySQL
- ? knex.raw("group_concat(address SEPARATOR ', ') AS domains")
- : knex.raw("string_agg(address, ', ') AS domains")
- )
- .groupBy("user_id").as("d"),
- "users.id",
- "d.user_id"
- );
- }
- if (params?.links !== undefined) {
- query.andWhere("links", params?.links ? "is not" : "is", null);
- query.leftJoin(
- knex("links").select("user_id").count("* as links").groupBy("user_id").as("l"),
- "users.id",
- "l.user_id"
- );
- }
- const [{ count }] = await query;
- return typeof count === "number" ? count : parseInt(count);
- }
- async function create(params) {
- let [user] = await knex("users").insert({
- email: params.email,
- password: params.password,
- role: params.role ?? ROLES.USER,
- verified: params.verified ?? false,
- banned: params.banned ?? false,
- }, "*");
- // mysql doesn't return the whole user, but rather the id number only
- // so we need to fetch the user ourselves
- if (typeof user === "number") {
- user = await knex("users").where("id", user).first();
- }
- return user;
- }
- // check if there exists a user
- async function findAny() {
- if (env.REDIS_ENABLED) {
- const anyuser = await redis.client.get("any-user");
- if (anyuser) return true;
- }
- const anyuser = await knex("users").select("id").first();
- if (env.REDIS_ENABLED && anyuser) {
- redis.client.set("any-user", JSON.stringify(anyuser), "EX", 60 * 5);
- }
- return !!anyuser;
- }
- module.exports = {
- add,
- create,
- find,
- findAny,
- getAdmin,
- remove,
- totalAdmin,
- update,
- }
|