Feat : create games

This commit is contained in:
2024-06-06 19:50:29 -04:00
parent 30e9a3efcb
commit e870a254ed
18 changed files with 379 additions and 82 deletions
+6 -9
View File
@@ -1,19 +1,16 @@
import { PartialType } from '@nestjs/mapped-types';
import { CreateUserDto } from './create-user.dto';
import { ApiProperty } from '@nestjs/swagger';
export class LoginDto extends PartialType(CreateUserDto) {
constructor(id: string, global_name: string, avatar: string) {
super();
this.id = id;
this.global_name = global_name;
export class LoginDto {
constructor(userId: string, username: string, avatar: string) {
this.userId = userId;
this.username = username;
this.avatar = avatar;
}
@ApiProperty()
id: string;
userId: string;
@ApiProperty()
global_name: string;
username: string;
@ApiProperty()
avatar: string;
+12 -3
View File
@@ -1,9 +1,18 @@
import { Schema, SchemaFactory } from '@nestjs/mongoose';
import { HydratedDocument } from 'mongoose';
import { Prop, Schema, SchemaFactory } from '@nestjs/mongoose';
import { HydratedDocument, Types } from 'mongoose';
export type UserDocument = HydratedDocument<User>;
@Schema()
export class User {}
export class User extends Types.ObjectId {
@Prop()
userId: string;
@Prop()
avatar: string;
@Prop()
username: string;
}
export const UserSchema = SchemaFactory.createForClass(User);
+4 -11
View File
@@ -19,13 +19,9 @@ export class UsersController {
async login(
@Query('code') code: string,
@Session() session: Record<string, any>,
) {
const tokenData = await this.usersService.saveDiscordToken(code);
session.tokenData = tokenData;
const user = await this.usersService.useDiscordToken(
tokenData.token_type,
tokenData.token,
);
): Promise<LoginDto> {
await this.usersService.createDiscordToken({ code }, session);
const user = await this.usersService.discordLogin(session);
return user;
}
@@ -37,10 +33,7 @@ export class UsersController {
HttpStatus.UNAUTHORIZED,
);
}
const user = await this.usersService.useDiscordToken(
session.tokenData.token_type,
session.tokenData.token,
);
const user = await this.usersService.discordLogin(session);
return user;
}
}
+94 -25
View File
@@ -1,30 +1,67 @@
import { HttpException, HttpStatus, Injectable } from '@nestjs/common';
import { LoginDto } from './dto/login.dto';
import { URLSearchParams } from 'url';
import { User } from './schemas/user.entity';
import { Model } from 'mongoose';
import { InjectModel } from '@nestjs/mongoose';
@Injectable()
export class UsersService {
async saveDiscordToken(code: string) {
constructor(@InjectModel(User.name) private userModel: Model<User>) {}
async createDiscordToken(
{
code,
refresh_token,
}: {
code?: string;
refresh_token?: string;
},
session: Record<string, any>,
): Promise<void> {
let body;
if (code) {
body = new URLSearchParams({
client_id: process.env.DISCORD_CLIENTID,
client_secret: process.env.DISCORD_CLIENTSECRET,
code,
grant_type: 'authorization_code',
redirect_uri: process.env.API_ENDPOINT + '/login',
});
} else if (refresh_token) {
body = new URLSearchParams({
client_id: process.env.DISCORD_CLIENTID,
client_secret: process.env.DISCORD_CLIENTSECRET,
refresh_token,
grant_type: 'refresh_token',
});
} else {
throw new HttpException(
'Invalid internal call',
HttpStatus.INTERNAL_SERVER_ERROR,
);
}
const req = await fetch('https://discord.com/api/oauth2/token', {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
body: new URLSearchParams({
client_id: process.env.DISCORD_CLIENTID,
client_secret: process.env.DISCORD_CLIENTSECRET,
code: code,
grant_type: 'authorization_code',
redirect_uri: process.env.API_ENDPOINT + '/login',
scope: 'identify',
}),
body,
});
if (!req.ok) {
console.error(await req.json());
throw new HttpException(
"Couldn't fetch Discord api",
HttpStatus.INTERNAL_SERVER_ERROR,
);
const data = await req.json();
if (data.error == 'invalid_grant') {
throw new HttpException(
'Invalid code in request',
HttpStatus.UNAUTHORIZED,
);
} else {
console.error(data);
throw new HttpException(
"Couldn't fetch Discord api",
HttpStatus.INTERNAL_SERVER_ERROR,
);
}
}
const data = await req.json();
if (!data.token_type || !data.access_token) {
@@ -34,27 +71,59 @@ export class UsersService {
HttpStatus.INTERNAL_SERVER_ERROR,
);
}
const token_type = data.token_type;
const token = data.access_token;
return { token, token_type };
const expires = new Date(Date.now() + data.expires_in);
session.tokenData = {
access_token: data.access_token,
token_type: data.token_type,
refresh_token: data.refresh_token,
expires,
};
}
async useDiscordToken(token_type: string, token: string) {
async discordLogin(
session: Record<string, any>,
recursive: boolean = false,
): Promise<LoginDto> {
const req2 = await fetch('https://discord.com/api/users/@me', {
headers: {
authorization: token_type + ' ' + token,
authorization:
session.tokenData.token_type + ' ' + session.tokenData.access_token,
},
});
if (!req2.ok) {
throw new HttpException(
"Couldn't fetch Discord api",
HttpStatus.INTERNAL_SERVER_ERROR,
);
const error = await req2.json();
if (error.error == 'invalid_grant' && !recursive) {
await this.createDiscordToken(
{
refresh_token: session.tokenData.refresh_token,
},
session,
);
return this.discordLogin(session);
} else {
console.error(error);
throw new HttpException(
"Couldn't fetch Discord api",
HttpStatus.INTERNAL_SERVER_ERROR,
);
}
}
const data2 = await req2.json();
return new LoginDto(data2.id, data2.global_name, data2.avatar);
console.log(data2);
const dto = new LoginDto(data2.id, data2.global_name, data2.avatar);
const user = await this.getOrCreateUser(dto.userId, dto);
session.user = user;
return dto;
}
async getOrCreateUser(userId: string, data?: LoginDto) {
let user = await this.userModel.findOne({ userId }).exec();
if (!user) {
const createdUser = new this.userModel({ ...data, userId });
user = await createdUser.save();
}
return user;
}
remove(id: number) {