【问题标题】:db migration User entity in Loopback4Loopback4 中的数据库迁移用户实体
【发布时间】:2020-11-11 01:00:32
【问题描述】:

我按照 Loopback 4 教程使用 @loopback/authentication、@loopback/authentication-jwt 来保护 Api。我创建了一个实体 todo 以遵循教程示例,当我迁移 db 时,只创建了 todo 表,但用户、UserCredential 相关的表无法自动迁移,官方示例在github 上给出它工作正常但它在内存数据库中使用文件。

有没有办法通过使用@loopback/authentication-jwt/User 实体进行迁移来创建表?

【问题讨论】:

    标签: javascript loopback4


    【解决方案1】:

    我在同一个问题上卡住了很长一段时间,但最终想出了如何为 MySQL 做这件事。它的要点是您需要创建自己的模型,然后覆盖 @loopback/authentication-jwt 扩展中内置的 User 和 UserCredentials 的服务和模型。这个其实在github上有说明here

    在我的例子中,让这件事变得不平凡的是,我需要弄清楚如何将扩展中定义的模型转换为您可以实际存储在 MySQL 中的数据模型。这样做后,如果它们位于模型文件夹中,它们将正确迁移。

    以下是我为使其正常工作所做的一些示例:

    在application.ts的构造函数中

    // Mount authentication system
    this.component(AuthenticationComponent);
    // Mount jwt component
    this.component(JWTAuthenticationComponent);
    // Bind datasource
    this.dataSource(NihongoWordListDataSource, UserServiceBindings.DATASOURCE_NAME);
    // Bind user service
    this.bind(UserServiceBindings.USER_SERVICE).toClass(AppUserService),
    // Bind user and credentials repository
    this.bind(UserServiceBindings.USER_REPOSITORY).toClass(
      AppUserRepository,
    ),
    this.bind(UserServiceBindings.USER_CREDENTIALS_REPOSITORY).toClass(
      AppUserCredentialsRepository,
    ),
    

    自定义用户服务(基本上是直接从github页面复制过来的,只有我的叫AppUser):

        import {UserService} from '@loopback/authentication';
        import {repository} from '@loopback/repository';
        import {HttpErrors} from '@loopback/rest';
        import {securityId, UserProfile} from '@loopback/security';
        import {compare} from 'bcryptjs';
        // User --> MyUser
        import {AppUser} from '../models';
        // UserRepository --> MyUserRepository
        import { AppUserRepository } from '../repositories';
        
        export type Credentials = {
          email: string;
          password: string;
        };
        
        // User --> MyUser
        export class AppUserService implements UserService<AppUser, Credentials> {
          constructor(
            // UserRepository --> MyUserRepository
            @repository(AppUserRepository) public userRepository: AppUserRepository,
          ) {}
    
        // User --> MyUser
          async verifyCredentials(credentials: Credentials): Promise<AppUser> {
            const invalidCredentialsError = 'Invalid email or password.';
    
        const foundUser = await this.userRepository.findOne({
          where: {email: credentials.email},
        });
        if (!foundUser) {
          throw new HttpErrors.Unauthorized(invalidCredentialsError);
        }
    
        const credentialsFound = await this.userRepository.findCredentials(
          foundUser.id,
        );
        if (!credentialsFound) {
          throw new HttpErrors.Unauthorized(invalidCredentialsError);
        }
    
        const passwordMatched = await compare(
          credentials.password,
          credentialsFound.password,
        );
    
        if (!passwordMatched) {
          throw new HttpErrors.Unauthorized(invalidCredentialsError);
        }
    
        return foundUser;
      }
    
        // User --> MyUser
          convertToUserProfile(user: AppUser): UserProfile {
            return {
              [securityId]: user.id.toString(),
              name: user.username,
              id: user.id,
              email: user.email,
            };
          }
        }
    

    AppUser 模型:

    import {Entity, model, property, hasOne} from '@loopback/repository';
    import {AppUserCredentials} from './app-user-credentials.model';
    
    @model({
      settings: {
        idInjection: false,
        mysql: {schema: 'nihongo_word_list', table: 'app_user'}
      }
    })
    export class AppUser extends Entity {
      @property({
        type: 'string',
        id: true,
        generated: false,
        defaultFn: 'uuidv4',
        mysql: {columnName: 'id', dataType: 'varchar', dataLength: 36, nullable: 'N'},
      })
      id: string;
    
      @property({
        type: 'string',
        mysql: {columnName: 'realm', dataType: 'varchar', dataLength: 36, nullable: 'Y'},
      })
      realm?: string;
    
      @property({
        type: 'string',
        mysql: {columnName: 'username', dataType: 'varchar', dataLength: 256, nullable: 'Y'},
      })
      username?: string;
    
      @property({
        type: 'string',
        required: true,
        mysql: {columnName: 'email', dataType: 'varchar', dataLength: 256, nullable: 'N'},
      })
      email: string;
    
      @property({
        type: 'boolean',
        required: true,
        mysql: {columnName: 'emailVerified', dataType: 'tinyint', dataLength: 1, nullable: 'Y', default: 0},
      })
      emailVerified?: boolean;
    
      @property({
        type: 'string',
        mysql: {columnName: 'verificationToken', dataType: 'varchar', dataLength: 256, nullable: 'N'},
      })
      verificationToken?: string;
    
      @hasOne(() => AppUserCredentials, {keyTo: 'userId'})
      userCredentials: AppUserCredentials;
    
      // Define well-known properties here
    
      // Indexer property to allow additional data
      // eslint-disable-next-line @typescript-eslint/no-explicit-any
      [prop: string]: any;
    
      constructor(data?: Partial<AppUser>) {
        super(data);
      }
    }
    
    export interface AppUserRelations {
      // describe navigational properties here
    }
    
    export type AppUserWithRelations = AppUser & AppUserRelations;
    

    您需要对 AppUserCredentials 执行相同的操作。

    您还需要为 AppUser 和 AppUserCredentials 模型设置存储库。

    我希望这能让您走上正轨。请注意,在对迁移进行更改时,您可以并且应该使用以下 npm CLI 方法。我刚刚这样做了:

    // clean your dist directory otherwise built stuff gets left behind and can prevent your app from starting
    npm run clean
    // rebuild your code into dist folder
    npm run build
    // migrate your db
    npm run migrate
    

    祝你好运!

    【讨论】:

      猜你喜欢
      • 2014-07-08
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-04-29
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多