external-account.ts 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151
  1. // disable no-return-await for model functions
  2. /* eslint-disable no-return-await */
  3. import type { IExternalAccount, IExternalAccountHasId, IUserHasId } from '@growi/core';
  4. import type { Model, Document } from 'mongoose';
  5. import { Schema } from 'mongoose';
  6. import { NullUsernameToBeRegisteredError } from '~/server/models/errors';
  7. import { getOrCreateModel } from '../util/mongoose-utils';
  8. const debug = require('debug')('growi:models:external-account');
  9. const mongoose = require('mongoose');
  10. const mongoosePaginate = require('mongoose-paginate-v2');
  11. const uniqueValidator = require('mongoose-unique-validator');
  12. export interface ExternalAccountDocument extends IExternalAccount, Document {}
  13. export interface ExternalAccountModel extends Model<ExternalAccountDocument> {
  14. [x:string]: any, // for old methods
  15. }
  16. const schema = new Schema<ExternalAccountDocument, ExternalAccountModel>({
  17. providerType: { type: String, required: true },
  18. accountId: { type: String, required: true },
  19. user: { type: Schema.Types.ObjectId, ref: 'User', required: true },
  20. }, {
  21. timestamps: { createdAt: true, updatedAt: false },
  22. });
  23. // compound index
  24. schema.index({ providerType: 1, accountId: 1 }, { unique: true });
  25. // apply plugins
  26. schema.plugin(mongoosePaginate);
  27. schema.plugin(uniqueValidator);
  28. /**
  29. * limit items num for pagination
  30. */
  31. const DEFAULT_LIMIT = 50;
  32. /**
  33. * The Exception class thrown when User.username is duplicated when creating user
  34. *
  35. * @class DuplicatedUsernameException
  36. */
  37. class DuplicatedUsernameException {
  38. name: string;
  39. message: string;
  40. user: IUserHasId;
  41. constructor(message, user) {
  42. this.name = this.constructor.name;
  43. this.message = message;
  44. this.user = user;
  45. }
  46. }
  47. /**
  48. * find an account or register if not found
  49. */
  50. schema.statics.findOrRegister = function(
  51. isSameUsernameTreatedAsIdenticalUser: boolean,
  52. isSameEmailTreatedAsIdenticalUser: boolean,
  53. providerType: string,
  54. accountId: string,
  55. usernameToBeRegistered?: string,
  56. nameToBeRegistered?: string,
  57. mailToBeRegistered?: string,
  58. ): Promise<IExternalAccountHasId> {
  59. return this.findOne({ providerType, accountId })
  60. .then((account) => {
  61. // ExternalAccount is found
  62. if (account != null) {
  63. debug(`ExternalAccount '${accountId}' is found `, account);
  64. return account;
  65. }
  66. if (usernameToBeRegistered == null) {
  67. throw new NullUsernameToBeRegisteredError('username_should_not_be_null');
  68. }
  69. const User = mongoose.model('User');
  70. let promise = User.findOne({ username: usernameToBeRegistered });
  71. if (isSameUsernameTreatedAsIdenticalUser && isSameEmailTreatedAsIdenticalUser) {
  72. promise = promise
  73. .then((user) => {
  74. if (user == null) { return User.findOne({ email: mailToBeRegistered }) }
  75. return user;
  76. });
  77. }
  78. else if (isSameEmailTreatedAsIdenticalUser) {
  79. promise = User.findOne({ email: mailToBeRegistered });
  80. }
  81. return promise
  82. .then((user) => {
  83. // when the User that have the same `username` exists
  84. if (user != null) {
  85. throw new DuplicatedUsernameException(`User '${usernameToBeRegistered}' already exists`, user);
  86. }
  87. if (nameToBeRegistered == null) {
  88. // eslint-disable-next-line no-param-reassign
  89. nameToBeRegistered = '';
  90. }
  91. // create a new User with STATUS_ACTIVE
  92. debug(`ExternalAccount '${accountId}' is not found, it is going to be registered.`);
  93. return User.createUser(nameToBeRegistered, usernameToBeRegistered, mailToBeRegistered, undefined, undefined, User.STATUS_ACTIVE);
  94. })
  95. .then((newUser) => {
  96. return this.associate(providerType, accountId, newUser);
  97. });
  98. });
  99. };
  100. /**
  101. * Create ExternalAccount document and associate to existing User
  102. */
  103. schema.statics.associate = function(providerType: string, accountId: string, user: IUserHasId) {
  104. return this.create({ providerType, accountId, user: user._id });
  105. };
  106. /**
  107. * find all entities with pagination
  108. *
  109. * @see https://github.com/edwardhotchkiss/mongoose-paginate
  110. *
  111. * @static
  112. * @param {any} opts mongoose-paginate options object
  113. * @returns {Promise<any>} mongoose-paginate result object
  114. * @memberof ExternalAccount
  115. */
  116. schema.statics.findAllWithPagination = function(opts) {
  117. const query = {};
  118. const options = Object.assign({ populate: 'user' }, opts);
  119. if (options.sort == null) {
  120. options.sort = { accountId: 1, createdAt: 1 };
  121. }
  122. if (options.limit == null) {
  123. options.limit = DEFAULT_LIMIT;
  124. }
  125. return this.paginate(query, options);
  126. };
  127. export default getOrCreateModel<ExternalAccountDocument, ExternalAccountModel>('ExternalAccount', schema);