Initial commit
This commit is contained in:
@@ -0,0 +1,5 @@
|
||||
export class CreateUserDto {
|
||||
method: 'discord' | 'password';
|
||||
username?: string;
|
||||
password: string;
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
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;
|
||||
this.avatar = avatar;
|
||||
}
|
||||
@ApiProperty()
|
||||
id: string;
|
||||
|
||||
@ApiProperty()
|
||||
global_name: string;
|
||||
|
||||
@ApiProperty()
|
||||
avatar: string;
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
import { Schema, SchemaFactory } from '@nestjs/mongoose';
|
||||
import { HydratedDocument } from 'mongoose';
|
||||
|
||||
export type UserDocument = HydratedDocument<User>;
|
||||
|
||||
@Schema()
|
||||
export class User {}
|
||||
|
||||
export const UserSchema = SchemaFactory.createForClass(User);
|
||||
@@ -0,0 +1,20 @@
|
||||
import { Test, TestingModule } from '@nestjs/testing';
|
||||
import { UsersController } from './users.controller';
|
||||
import { UsersService } from './users.service';
|
||||
|
||||
describe('UsersController', () => {
|
||||
let controller: UsersController;
|
||||
|
||||
beforeEach(async () => {
|
||||
const module: TestingModule = await Test.createTestingModule({
|
||||
controllers: [UsersController],
|
||||
providers: [UsersService],
|
||||
}).compile();
|
||||
|
||||
controller = module.get<UsersController>(UsersController);
|
||||
});
|
||||
|
||||
it('should be defined', () => {
|
||||
expect(controller).toBeDefined();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,46 @@
|
||||
import {
|
||||
Controller,
|
||||
Get,
|
||||
Query,
|
||||
Session,
|
||||
HttpException,
|
||||
HttpStatus,
|
||||
} from '@nestjs/common';
|
||||
import { UsersService } from './users.service';
|
||||
import { LoginDto } from './dto/login.dto';
|
||||
import { ApiTags } from '@nestjs/swagger';
|
||||
|
||||
@ApiTags('auth')
|
||||
@Controller()
|
||||
export class UsersController {
|
||||
constructor(private readonly usersService: UsersService) {}
|
||||
|
||||
@Get('/login')
|
||||
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,
|
||||
);
|
||||
return user;
|
||||
}
|
||||
|
||||
@Get('/whoami')
|
||||
async remove(@Session() session: Record<string, any>): Promise<LoginDto> {
|
||||
if (!session.tokenData) {
|
||||
throw new HttpException(
|
||||
"Session doesn't contains auth information",
|
||||
HttpStatus.UNAUTHORIZED,
|
||||
);
|
||||
}
|
||||
const user = await this.usersService.useDiscordToken(
|
||||
session.tokenData.token_type,
|
||||
session.tokenData.token,
|
||||
);
|
||||
return user;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
import { UsersService } from './users.service';
|
||||
import { UsersController } from './users.controller';
|
||||
import { MongooseModule } from '@nestjs/mongoose';
|
||||
import { User, UserSchema } from './schemas/user.entity';
|
||||
|
||||
@Module({
|
||||
imports: [
|
||||
MongooseModule.forFeature([{ name: User.name, schema: UserSchema }]),
|
||||
],
|
||||
controllers: [UsersController],
|
||||
providers: [UsersService],
|
||||
exports: [MongooseModule],
|
||||
})
|
||||
export class UsersModule {
|
||||
schema = UserSchema;
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
import { Test, TestingModule } from '@nestjs/testing';
|
||||
import { UsersService } from './users.service';
|
||||
|
||||
describe('UsersService', () => {
|
||||
let service: UsersService;
|
||||
|
||||
beforeEach(async () => {
|
||||
const module: TestingModule = await Test.createTestingModule({
|
||||
providers: [UsersService],
|
||||
}).compile();
|
||||
|
||||
service = module.get<UsersService>(UsersService);
|
||||
});
|
||||
|
||||
it('should be defined', () => {
|
||||
expect(service).toBeDefined();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,63 @@
|
||||
import { HttpException, HttpStatus, Injectable } from '@nestjs/common';
|
||||
import { LoginDto } from './dto/login.dto';
|
||||
import { URLSearchParams } from 'url';
|
||||
|
||||
@Injectable()
|
||||
export class UsersService {
|
||||
async saveDiscordToken(code: string) {
|
||||
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',
|
||||
}),
|
||||
});
|
||||
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.token_type || !data.access_token) {
|
||||
console.error(data);
|
||||
throw new HttpException(
|
||||
'Invalid Discord API response',
|
||||
HttpStatus.INTERNAL_SERVER_ERROR,
|
||||
);
|
||||
}
|
||||
const token_type = data.token_type;
|
||||
const token = data.access_token;
|
||||
|
||||
return { token, token_type };
|
||||
}
|
||||
|
||||
async useDiscordToken(token_type: string, token: string) {
|
||||
const req2 = await fetch('https://discord.com/api/users/@me', {
|
||||
headers: {
|
||||
authorization: token_type + ' ' + token,
|
||||
},
|
||||
});
|
||||
|
||||
if (!req2.ok) {
|
||||
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);
|
||||
}
|
||||
|
||||
remove(id: number) {
|
||||
return `This action removes a #${id} user`;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user