diff --git a/packages/bridge-server/migrations/20231220114130-create-user.js b/packages/bridge-server/migrations/20231220114130-create-user.js new file mode 100644 index 0000000..3c2d71b --- /dev/null +++ b/packages/bridge-server/migrations/20231220114130-create-user.js @@ -0,0 +1,36 @@ +'use strict'; +/** @type {import('sequelize-cli').Migration} */ +module.exports = { + async up(queryInterface, Sequelize) { + await queryInterface.createTable('Users', { + auth0id: { + allowNull: false, + primaryKey: true, + type: Sequelize.STRING + }, + nickname: { + type: Sequelize.STRING + }, + picture: { + type: Sequelize.STRING + }, + realName:{ + type: Sequelize.STRING + }, + lastLogin: { + type: Sequelize.DATE + }, + createdAt: { + allowNull: false, + type: Sequelize.DATE + }, + updatedAt: { + allowNull: false, + type: Sequelize.DATE + } + }); + }, + async down(queryInterface, Sequelize) { + await queryInterface.dropTable('Users'); + } +}; \ No newline at end of file diff --git a/packages/bridge-server/models/user.js b/packages/bridge-server/models/user.js new file mode 100644 index 0000000..7fc115a --- /dev/null +++ b/packages/bridge-server/models/user.js @@ -0,0 +1,27 @@ +'use strict'; +const { + Model +} = require('sequelize'); +module.exports = (sequelize, DataTypes) => { + class User extends Model { + /** + * Helper method for defining associations. + * This method is not a part of Sequelize lifecycle. + * The `models/index` file will call this method automatically. + */ + static associate(models) { + // define association here + } + } + User.init({ + nickname: DataTypes.STRING, + auth0id: DataTypes.STRING, + picture: DataTypes.STRING, + realName: DataTypes.STRING, + lastLogin: DataTypes.DATE + }, { + sequelize, + modelName: 'User', + }); + return User; +}; \ No newline at end of file diff --git a/packages/bridge-server/src/auth/auth.service.ts b/packages/bridge-server/src/auth/auth.service.ts index 0c8cb97..7b359fb 100644 --- a/packages/bridge-server/src/auth/auth.service.ts +++ b/packages/bridge-server/src/auth/auth.service.ts @@ -12,13 +12,6 @@ export class AuthService { ) {} async signIn(username: string, pass: string): Promise { - const user = await this.usersService.findOne(username); - if (user?.password !== pass) { - throw new UnauthorizedException(); - } - const payload = { sub: user.userId, username: user.username }; - return { - access_token: await this.jwtService.signAsync(payload), - }; + } } \ No newline at end of file diff --git a/packages/bridge-server/src/users/users.controller.ts b/packages/bridge-server/src/users/users.controller.ts new file mode 100644 index 0000000..ed0a350 --- /dev/null +++ b/packages/bridge-server/src/users/users.controller.ts @@ -0,0 +1,21 @@ +import { Body, Controller, Get, Param, Post, UseGuards } from '@nestjs/common'; +import { JwtAuthGuard } from 'src/authz/authz.guard'; +import { UsersService } from './users.service'; + +@Controller('users') +export class UsersController { + constructor( + private usersService: UsersService + ) {} + + @Get(':id') + findOne(@Param() params: any) { + //return this.seasonsService.findOne(params.id); + } + + @UseGuards(JwtAuthGuard) + @Post('login') + updateLastLogin(@Body() body: any) { + return this.usersService.updateLastLogin(body.id, body.nickname, body.picture, body.time); + } +} diff --git a/packages/bridge-server/src/users/users.model.ts b/packages/bridge-server/src/users/users.model.ts new file mode 100644 index 0000000..16dc9aa --- /dev/null +++ b/packages/bridge-server/src/users/users.model.ts @@ -0,0 +1,20 @@ +import { Column, HasMany, Model, PrimaryKey, Table } from "sequelize-typescript"; + +@Table +export class User extends Model { + @PrimaryKey + @Column + auth0id: string; + + @Column + nickname: string; + + @Column + picture: string; + + @Column + realName: string; + + @Column + lastLogin: Date; +} \ No newline at end of file diff --git a/packages/bridge-server/src/users/users.module.ts b/packages/bridge-server/src/users/users.module.ts index 8fa904f..8e9efb6 100644 --- a/packages/bridge-server/src/users/users.module.ts +++ b/packages/bridge-server/src/users/users.module.ts @@ -1,8 +1,15 @@ import { Module } from '@nestjs/common'; +import { SequelizeModule } from '@nestjs/sequelize'; import { UsersService } from './users.service'; +import { UsersController } from './users.controller'; +import { User } from './users.model'; @Module({ + imports: [ + SequelizeModule.forFeature([User]), + ], providers: [UsersService], - exports: [UsersService], + controllers: [UsersController], + exports: [SequelizeModule, UsersService], }) export class UsersModule {} diff --git a/packages/bridge-server/src/users/users.service.ts b/packages/bridge-server/src/users/users.service.ts index eb6ff83..76bd512 100644 --- a/packages/bridge-server/src/users/users.service.ts +++ b/packages/bridge-server/src/users/users.service.ts @@ -1,24 +1,25 @@ import { Injectable } from '@nestjs/common'; +import { InjectModel } from '@nestjs/sequelize'; +import { Sequelize } from 'sequelize-typescript'; +import { User } from './users.model'; // This should be a real class/interface representing a user entity -export type User = any; +//export type User = any; @Injectable() export class UsersService { - private readonly users = [ - { - userId: 1, - username: 'john', - password: 'changeme', - }, - { - userId: 2, - username: 'maria', - password: 'guess', - }, - ]; async findOne(username: string): Promise { - return this.users.find(user => user.username === username); + return undefined; + } + + constructor( + @InjectModel(User) private userModel: typeof User, + private sequelize: Sequelize + ) + {} + + updateLastLogin(id, nickname, picture, time) { + this.userModel.upsert({auth0id: id, nickname: nickname, picture: picture, lastLogin: time}); } } \ No newline at end of file diff --git a/packages/bridge-ui/src/app/app.config.ts b/packages/bridge-ui/src/app/app.config.ts index b42aa88..1011b5f 100644 --- a/packages/bridge-ui/src/app/app.config.ts +++ b/packages/bridge-ui/src/app/app.config.ts @@ -61,6 +61,17 @@ export const appConfig: ApplicationConfig = { audience: 'https://ponyta.pkmn.cloud' } } + }, + { + uriMatcher: (uri) => { + let is_login = uri.match('.+\/login'); + return is_login != null; + }, + tokenOptions: { + authorizationParams: { + audience: 'https://ponyta.pkmn.cloud' + } + } } ] } diff --git a/packages/bridge-ui/src/app/services/users.service.ts b/packages/bridge-ui/src/app/services/users.service.ts index 1d02d85..dca4343 100644 --- a/packages/bridge-ui/src/app/services/users.service.ts +++ b/packages/bridge-ui/src/app/services/users.service.ts @@ -1,7 +1,9 @@ +import { HttpClient } from '@angular/common/http'; import { Injectable } from '@angular/core'; import { AuthService, User } from '@auth0/auth0-angular'; import * as jwt_decode from 'jwt-decode'; +import { ServerEndpointService } from './server-endpoint.service'; @Injectable({ providedIn: 'root' @@ -14,6 +16,8 @@ export class UsersService { constructor( public authService: AuthService, + private httpClient: HttpClient, + private serverEndpointService: ServerEndpointService, ) { } private hasPermission(permission: string) { @@ -29,6 +33,8 @@ export class UsersService { this.authService.user$.subscribe(data => { this.user = data; + console.log(this.user); + this.reportLogin(); }) this.authService.getAccessTokenSilently().subscribe(data => { @@ -43,6 +49,15 @@ export class UsersService { }) } + reportLogin() { + if(!this.user) { return } + let id = this.user.sub; + let nickname = this.user.nickname; + let picture = this.user.picture; + let time = Date.now(); + this.httpClient.post(this.serverEndpointService.getCurrentEndpoint()+"users/login", {id, nickname, picture, time}).subscribe(); + } + getUserName() :string { if(!this.isAuthenticated || !this.user) {return ""} @@ -64,4 +79,6 @@ export class UsersService { canCreateRaces(): boolean { return this.hasPermission("create:races") } + + }