db/models/user.jsjavascript
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const sequelize_1 = require("sequelize");
const userAccess_1 = __importDefault(require("./userAccess"));
const db_1 = __importDefault(require("../db"));
const passport_1 = require("../../plugins/passport.js");
const bcrypt = require("bcrypt");
/**
 * @openapi
 * components:
 *  schemas:
 *    User:
 *      type: object
 *      required:
 *      - id
 *      - username
 *      properties:
 *        id:
 *          type: string
 *          format: uuid
 *        username:
 *          type: string
 *        email:
 *          type: string
 *          format: email
 *        emailConfirmed:
 *          type: boolean
 *          default: false
 *        profilePictureExtension:
 *          type: string
 *          example: .png
 *        password:
 *          type: string
 *          format: password
 *        lastLoggedIn:
 *          type: string
 *          format: date-time
 */
/**
 * @typedef User
 * @property {UUID} id
 * @property {string} username
 * @property {string} [email]
 * @property {boolean} [emailConfirmed]
 * @property {string} [profilePictureExtension]
 * @property {string} password
 * @property {Date} [lastLoggedIn]
 * @memberof module:Models
 */
class User extends sequelize_1.Model {
}
User.init({
    id: {
        type: sequelize_1.DataTypes.UUID,
        defaultValue: sequelize_1.DataTypes.UUIDV4,
        primaryKey: true,
    },
    username: {
        type: sequelize_1.DataTypes.STRING,
        allowNull: true,
        validate: {
            len: {
                args: [6, 999],
                msg: "Username has to be at least 6 characters",
            },
        },
    },
    email: {
        type: sequelize_1.DataTypes.STRING,
        allowNull: true,
        unique: true,
        validate: {
            isEmail: {
                msg: "Email has to be correct email format, e.g. info@choreo-planer.de",
            },
        },
    },
    emailConfirmed: {
        type: sequelize_1.DataTypes.BOOLEAN,
        defaultValue: false,
        allowNull: false,
    },
    profilePictureExtension: {
        type: sequelize_1.DataTypes.STRING,
        allowNull: true,
    },
    password: {
        type: sequelize_1.DataTypes.STRING,
        allowNull: true,
        set(value) {
            if (value == null) {
                this.setDataValue("password", undefined);
            }
            else {
                const salt = bcrypt.genSaltSync(10, "a");
                this.setDataValue("password", bcrypt.hashSync(value, salt));
            }
        },
    },
    provider: {
        type: sequelize_1.DataTypes.ENUM(...Object.values(passport_1.AuthProvider)),
        allowNull: false,
        defaultValue: passport_1.AuthProvider.LOCAL,
        validate: {
            isIn: {
                args: [Object.values(passport_1.AuthProvider)],
                msg: "Provider must be one of: local, google, github, facebook",
            },
        },
    },
    socialId: {
        type: sequelize_1.DataTypes.STRING,
        allowNull: true,
    },
    lastLoggedIn: {
        type: sequelize_1.DataTypes.DATE,
        allowNull: true,
    },
    createdAt: sequelize_1.DataTypes.DATE,
    updatedAt: sequelize_1.DataTypes.DATE,
    deletedAt: sequelize_1.DataTypes.DATE,
}, {
    sequelize: db_1.default,
    defaultScope: {
        attributes: {
            exclude: ["password"],
        },
    },
    scopes: {
        withPasswordHash: {
            attributes: {
                exclude: [],
            },
        },
        includingDeleted: {
            paranoid: false,
        },
    },
    paranoid: true,
    hooks: {
        beforeValidate: function (instance) {
            if (instance.getDataValue("provider") === "local") {
                if (instance.getDataValue("username") == null) {
                    throw new Error("Username is required for local users");
                }
                if (instance.getDataValue("password") === null) {
                    throw new Error("Password is required for local users");
                }
            }
        },
        afterDestroy: async function (instance, _options) {
            const clubList = await instance.getClubs();
            await Promise.all(clubList.map((club) => club.destroy()));
            await userAccess_1.default.destroy({
                where: {
                    [sequelize_1.Op.or]: [
                        { ownerUserId: instance.id },
                        { childUserId: instance.id },
                    ],
                },
            });
        },
    },
    indexes: [
        // Enforce username uniqueness only for local provider users.
        {
            unique: true,
            fields: ["username"],
            where: {
                provider: passport_1.AuthProvider.LOCAL,
            },
        },
        {
            unique: true,
            fields: ["email", "provider"],
        },
        {
            unique: true,
            fields: ["socialId", "provider"],
        },
    ],
});
exports.default = User;