Skip to content

Commit

Permalink
Merge branch 'main' into add-chat-apis
Browse files Browse the repository at this point in the history
  • Loading branch information
Sma1lboy authored Oct 29, 2024
2 parents fc8929c + 450bdc7 commit 5528efe
Show file tree
Hide file tree
Showing 17 changed files with 565 additions and 43 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,4 @@ Still on progress

CodeFox
LOGO
![](./assets/WechatIMG1000.svg)
![](./assets/WechatIMG1000.svg)
23 changes: 13 additions & 10 deletions backend/src/app.module.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,19 @@
import { ApolloDriver, ApolloDriverConfig } from '@nestjs/apollo';
import { Module } from '@nestjs/common';
import { AppService } from './app.service';
import { ConfigModule } from '@nestjs/config';
import { GraphQLModule } from '@nestjs/graphql';
import { ApolloDriver, ApolloDriverConfig } from '@nestjs/apollo';
import { join } from 'path';
import { TypeOrmModule } from '@nestjs/typeorm';
import { UserModule } from './user/user.module';
import { User } from './user/user.model';
import { join } from 'path';
import { AuthModule } from './auth/auth.module';
import { ChatModule } from './chat/chat.module';
import { ProjectModule } from './project/project.module';
import { TokenModule } from './token/token.module';
import { ConfigModule, ConfigService } from '@nestjs/config';
import { JwtModule } from '@nestjs/jwt';
import { JwtCacheService } from './auth/jwt-cache.service';
import { ChatModule } from './chat/chat.module';
import { UserModule } from './user/user.module';
import { InitModule } from './init/init.module';
import { RolesGuard } from './guard/roles.guard';

Check warning on line 13 in backend/src/app.module.ts

View workflow job for this annotation

GitHub Actions / autofix

'RolesGuard' is defined but never used. Allowed unused vars must match /^_/u
import { MenuGuard } from './guard/menu.guard';

Check warning on line 14 in backend/src/app.module.ts

View workflow job for this annotation

GitHub Actions / autofix

'MenuGuard' is defined but never used. Allowed unused vars must match /^_/u
import { User } from './user/user.model';
import { AppResolver } from './app.resolver';

@Module({
imports: [
Expand All @@ -32,12 +33,14 @@ import { ChatModule } from './chat/chat.module';
logging: true,
entities: [__dirname + '/**/*.model{.ts,.js}'],
}),
InitModule,
UserModule,
AuthModule,
ProjectModule,
TokenModule,
ChatModule,
TypeOrmModule.forFeature([User]),
],
providers: [AppService],
providers: [AppResolver],
})
export class AppModule {}
12 changes: 12 additions & 0 deletions backend/src/app.resolver.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { Injectable } from '@nestjs/common';

Check warning on line 1 in backend/src/app.resolver.ts

View workflow job for this annotation

GitHub Actions / autofix

'Injectable' is defined but never used. Allowed unused vars must match /^_/u
import { Query, Resolver } from '@nestjs/graphql';
import { RequireRoles } from './decorator/auth.decorator';

@Resolver()
export class AppResolver {
@Query(() => String)
@RequireRoles('Admin')
getHello(): string {
return 'Hello World!';
}
}
8 changes: 0 additions & 8 deletions backend/src/app.service.ts

This file was deleted.

247 changes: 246 additions & 1 deletion backend/src/auth/auth.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import {
ConflictException,
Injectable,
Logger,
NotFoundException,
UnauthorizedException,
} from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
Expand All @@ -11,9 +12,11 @@ import { compare, hash } from 'bcrypt';
import { LoginUserInput } from 'src/user/dto/login-user.input';
import { RegisterUserInput } from 'src/user/dto/register-user.input';
import { User } from 'src/user/user.model';
import { Repository } from 'typeorm';
import { In, Repository } from 'typeorm';
import { CheckTokenInput } from './dto/check-token.input';
import { JwtCacheService } from 'src/auth/jwt-cache.service';
import { Menu } from './menu/menu.model';
import { Role } from './role/role.model';

@Injectable()
export class AuthService {
Expand All @@ -23,6 +26,10 @@ export class AuthService {
private jwtService: JwtService,
private jwtCacheService: JwtCacheService,
private configService: ConfigService,
@InjectRepository(Menu)
private menuRepository: Repository<Menu>,
@InjectRepository(Role)
private roleRepository: Repository<Role>,
) {}

async register(registerUserInput: RegisterUserInput): Promise<User> {
Expand Down Expand Up @@ -95,4 +102,242 @@ export class AuthService {
this.jwtCacheService.removeToken(token);
return true;
}

async assignMenusToRole(roleId: string, menuIds: string[]): Promise<Role> {
// Find the role with existing menus
const role = await this.roleRepository.findOne({
where: { id: roleId },
relations: ['menus'],
});

if (!role) {
throw new NotFoundException(`Role with ID "${roleId}" not found`);
}

// Find all menus
const menus = await this.menuRepository.findByIds(menuIds);

if (menus.length !== menuIds.length) {
throw new NotFoundException('Some menus were not found');
}

if (!role.menus) {
role.menus = [];
}

const newMenus = menus.filter(
(menu) => !role.menus.some((existingMenu) => existingMenu.id === menu.id),
);

if (newMenus.length === 0) {
throw new ConflictException(
'All specified menus are already assigned to this role',
);
}

role.menus.push(...newMenus);

try {
await this.roleRepository.save(role);
Logger.log(
`${newMenus.length} menus assigned to role ${role.name} successfully`,
);

return await this.roleRepository.findOne({
where: { id: roleId },
relations: ['menus'],
});
} catch (error) {
Logger.error(`Failed to assign menus to role: ${error.message}`);
throw error;
}
}

async removeMenuFromRole(roleId: string, menuId: string): Promise<Role> {
const role = await this.roleRepository.findOne({
where: { id: roleId },
relations: ['menus'],
});

if (!role) {
throw new NotFoundException(`Role with ID "${roleId}" not found`);
}

const menuIndex = role.menus?.findIndex((menu) => menu.id === menuId);

if (menuIndex === -1) {
throw new NotFoundException(
`Menu with ID "${menuId}" not found in role "${role.name}"`,
);
}

role.menus.splice(menuIndex, 1);

try {
await this.roleRepository.save(role);
Logger.log(`Menu removed from role ${role.name} successfully`);

return await this.roleRepository.findOne({
where: { id: roleId },
relations: ['menus'],
});
} catch (error) {
Logger.error(`Failed to remove menu from role: ${error.message}`);
throw error;
}
}

async assignRoles(userId: string, roleIds: string[]): Promise<User> {
const user = await this.userRepository.findOne({
where: { id: userId },
relations: ['roles'],
});

if (!user) {
throw new NotFoundException(`User with ID "${userId}" not found`);
}

const roles = await this.roleRepository.findBy({ id: In(roleIds) });

if (roles.length !== roleIds.length) {
throw new NotFoundException('Some roles were not found');
}

if (!user.roles) {
user.roles = [];
}

const newRoles = roles.filter(
(role) => !user.roles.some((existingRole) => existingRole.id === role.id),
);

if (newRoles.length === 0) {
throw new ConflictException(
'All specified roles are already assigned to this user',
);
}

user.roles.push(...newRoles);

try {
await this.userRepository.save(user);
Logger.log(
`${newRoles.length} roles assigned to user ${user.username} successfully`,
);

return await this.userRepository.findOne({
where: { id: userId },
relations: ['roles'],
});
} catch (error) {
Logger.error(`Failed to assign roles to user: ${error.message}`);
throw error;
}
}

async removeRoleFromUser(userId: string, roleId: string): Promise<User> {
const user = await this.userRepository.findOne({
where: { id: userId },
relations: ['roles'],
});

if (!user) {
throw new NotFoundException(`User with ID "${userId}" not found`);
}

const roleIndex = user.roles?.findIndex((role) => role.id === roleId);

if (roleIndex === -1) {
throw new NotFoundException(
`Role with ID "${roleId}" not found in user's roles`,
);
}

user.roles.splice(roleIndex, 1);

try {
await this.userRepository.save(user);
Logger.log(`Role removed from user ${user.username} successfully`);

return await this.userRepository.findOne({
where: { id: userId },
relations: ['roles'],
});
} catch (error) {
Logger.error(`Failed to remove role from user: ${error.message}`);
throw error;
}
}

async getUserRolesAndMenus(userId: string): Promise<{
roles: Role[];
menus: Menu[];
}> {
const user = await this.userRepository.findOne({
where: { id: userId },
relations: ['roles', 'roles.menus'],
});

if (!user) {
throw new NotFoundException(`User with ID "${userId}" not found`);
}

const userRoles = user.roles || [];

// Get unique menus across all roles
const userMenus = Array.from(
new Map(
userRoles
.flatMap((role) => role.menus || [])
.map((menu) => [menu.id, menu]),
).values(),
);

return {
roles: userRoles,
menus: userMenus,
};
}

async getUserRoles(userId: string): Promise<{
roles: Role[];
}> {
const user = await this.userRepository.findOne({
where: { id: userId },
relations: ['roles'],
});

if (!user) {
throw new NotFoundException(`User with ID "${userId}" not found`);
}

return {
roles: user.roles || [],
};
}

async getUserMenus(userId: string): Promise<{
menus: Menu[];
}> {
const user = await this.userRepository.findOne({
where: { id: userId },
relations: ['roles', 'roles.menus'],
});

if (!user) {
throw new NotFoundException(`User with ID "${userId}" not found`);
}

const userMenus = Array.from(
new Map(
(user.roles || [])
.flatMap((role) => role.menus || [])
.map((menu) => [menu.id, menu]),
).values(),
);

return {
menus: userMenus,
};
}
}
Loading

0 comments on commit 5528efe

Please sign in to comment.