2
0

password-reset-order.ts 2.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687
  1. import crypto from 'crypto';
  2. import { addMinutes } from 'date-fns/addMinutes';
  3. import type { Document, Model } from 'mongoose';
  4. import { Schema } from 'mongoose';
  5. import uniqueValidator from 'mongoose-unique-validator';
  6. import { getOrCreateModel } from '../util/mongoose-utils';
  7. export interface IPasswordResetOrder {
  8. token: string;
  9. email: string;
  10. // eslint-disable-next-line @typescript-eslint/no-explicit-any
  11. relatedUser: any;
  12. isRevoked: boolean;
  13. createdAt: Date;
  14. expiredAt: Date;
  15. }
  16. export interface PasswordResetOrderDocument
  17. extends IPasswordResetOrder,
  18. Document {
  19. isExpired(): boolean;
  20. revokeOneTimeToken(): Promise<void>;
  21. }
  22. export interface PasswordResetOrderModel
  23. extends Model<PasswordResetOrderDocument> {
  24. generateOneTimeToken(): string;
  25. createPasswordResetOrder(email: string): PasswordResetOrderDocument;
  26. }
  27. const expiredAt = (): Date => {
  28. return addMinutes(new Date(), 10);
  29. };
  30. const schema = new Schema<PasswordResetOrderDocument, PasswordResetOrderModel>(
  31. {
  32. token: { type: String, required: true, unique: true },
  33. email: { type: String, required: true },
  34. relatedUser: { type: Schema.Types.ObjectId, ref: 'User' },
  35. isRevoked: { type: Boolean, default: false, required: true },
  36. expiredAt: { type: Date, default: expiredAt, required: true },
  37. },
  38. {
  39. timestamps: {
  40. createdAt: true,
  41. updatedAt: false,
  42. },
  43. },
  44. );
  45. schema.plugin(uniqueValidator);
  46. schema.statics.generateOneTimeToken = () => {
  47. const buf = crypto.randomBytes(256);
  48. const token = buf.toString('hex');
  49. return token;
  50. };
  51. schema.statics.createPasswordResetOrder = async function (email) {
  52. let token: string;
  53. let duplicateToken: PasswordResetOrderDocument | null = null;
  54. do {
  55. token = this.generateOneTimeToken();
  56. // eslint-disable-next-line no-await-in-loop
  57. duplicateToken = await this.findOne({ token });
  58. } while (duplicateToken != null);
  59. const passwordResetOrderData = await this.create({ token, email });
  60. return passwordResetOrderData;
  61. };
  62. schema.methods.isExpired = function () {
  63. return this.expiredAt.getTime() < Date.now();
  64. };
  65. schema.methods.revokeOneTimeToken = async function () {
  66. this.isRevoked = true;
  67. return this.save();
  68. };
  69. export default getOrCreateModel<
  70. PasswordResetOrderDocument,
  71. PasswordResetOrderModel
  72. >('PasswordResetOrder', schema);