File size: 3,873 Bytes
f0743f4 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 | import type { QueryOptions } from 'mongoose';
import { IToken, TokenCreateData, TokenQuery, TokenUpdateData, TokenDeleteResult } from '~/types';
import logger from '~/config/winston';
// Factory function that takes mongoose instance and returns the methods
export function createTokenMethods(mongoose: typeof import('mongoose')) {
/**
* Creates a new Token instance.
*/
async function createToken(tokenData: TokenCreateData): Promise<IToken> {
try {
const Token = mongoose.models.Token;
const currentTime = new Date();
const expiresAt = new Date(currentTime.getTime() + tokenData.expiresIn * 1000);
const newTokenData = {
...tokenData,
createdAt: currentTime,
expiresAt,
};
return await Token.create(newTokenData);
} catch (error) {
logger.debug('An error occurred while creating token:', error);
throw error;
}
}
/**
* Updates a Token document that matches the provided query.
*/
async function updateToken(
query: TokenQuery,
updateData: TokenUpdateData,
): Promise<IToken | null> {
try {
const Token = mongoose.models.Token;
const dataToUpdate = { ...updateData };
if (updateData?.expiresIn !== undefined) {
dataToUpdate.expiresAt = new Date(Date.now() + updateData.expiresIn * 1000);
}
return await Token.findOneAndUpdate(query, dataToUpdate, { new: true });
} catch (error) {
logger.debug('An error occurred while updating token:', error);
throw error;
}
}
/**
* Deletes all Token documents that match the provided token, user ID, or email.
* Email is automatically normalized to lowercase for case-insensitive matching.
*/
async function deleteTokens(query: TokenQuery): Promise<TokenDeleteResult> {
try {
const Token = mongoose.models.Token;
const conditions = [];
if (query.userId !== undefined) {
conditions.push({ userId: query.userId });
}
if (query.token !== undefined) {
conditions.push({ token: query.token });
}
if (query.email !== undefined) {
conditions.push({ email: query.email.trim().toLowerCase() });
}
if (query.identifier !== undefined) {
conditions.push({ identifier: query.identifier });
}
/**
* If no conditions are specified, throw an error to prevent accidental deletion of all tokens
*/
if (conditions.length === 0) {
throw new Error('At least one query parameter must be provided');
}
return await Token.deleteMany({
$or: conditions,
});
} catch (error) {
logger.debug('An error occurred while deleting tokens:', error);
throw error;
}
}
/**
* Finds a Token document that matches the provided query.
* Email is automatically normalized to lowercase for case-insensitive matching.
*/
async function findToken(query: TokenQuery, options?: QueryOptions): Promise<IToken | null> {
try {
const Token = mongoose.models.Token;
const conditions = [];
if (query.userId) {
conditions.push({ userId: query.userId });
}
if (query.token) {
conditions.push({ token: query.token });
}
if (query.email) {
conditions.push({ email: query.email.trim().toLowerCase() });
}
if (query.identifier) {
conditions.push({ identifier: query.identifier });
}
const token = await Token.findOne({ $and: conditions }, null, options).lean();
return token as IToken | null;
} catch (error) {
logger.debug('An error occurred while finding token:', error);
throw error;
}
}
// Return all methods
return {
findToken,
createToken,
updateToken,
deleteTokens,
};
}
export type TokenMethods = ReturnType<typeof createTokenMethods>;
|