import { Document, InferSchemaType, Model, Schema, StringSchemaDefinition, Types } from 'mongoose'; import { STRATEGIES } from '../constants/strategies'; import { encrypt } from '../utils/password'; import { COLL_AUTH } from '../constants/db'; import { AuthDocument } from './auth'; import { Strategy } from '..'; export interface Strategy { method: STRATEGIES; parent: StringSchemaDefinition | AuthDocument; externalId?: string; key: string; profile?: { [key: string]: string | boolean | number }; forceReset?: boolean; } interface StrategyBaseDocument extends Strategy, Document { getAuthRecord(): Promise; getPopulatedStrategy(): Promise; } export interface StrategyDocument extends StrategyBaseDocument { parent: StringSchemaDefinition; } export interface StrategyPopulatedDocument extends StrategyBaseDocument { parent: AuthDocument; } export type StrategyModel = Model; export const StrategySchema = new Schema( { method: { type: Number, enum: STRATEGIES, index: true, }, externalId: { type: String, index: true }, forceReset: { type: Boolean }, key: { type: String, required: true, trim: true }, parent: { type: Types.ObjectId, ref: COLL_AUTH, required: true, }, profile: {}, }, { minimize: true, timestamps: true, }, ); StrategySchema.methods.getPopulatedStrategy = async function (this: StrategyDocument) { return this.populate('parent'); }; StrategySchema.methods.getAuthRecord = async function (this: StrategyDocument) { return (await this.getPopulatedStrategy()).parent; }; StrategySchema.pre('save', async function save(next) { if (typeof this.method === 'undefined') { return next(new Error(`Strategy requires a method.`)); } if (await Strategy.findOne({ method: this.method, parent: this.parent })) { return next(new Error(`${this.method} strategy already exists for this user.`)); } if (this.method !== STRATEGIES.LOCAL || !this.isModified('key')) { return next(); } this.key = encrypt(this.key); return next(); }); export type StrategySchema = InferSchemaType;