Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
f076fb9
feature: were added feature and feature-flag table
YuryHouse Mar 26, 2023
cf88fd6
feature: were linked feature and feature-flag tables
YuryHouse Mar 26, 2023
d7321b9
fix: was changed link between feature and feature-flag tables
YuryHouse Mar 27, 2023
ec9f46f
Merge pull request #76 from gitkoshelew/realize-candidate
gitkoshelew Mar 27, 2023
157002e
feature: added guide into README.md
YuryHouse Feb 6, 2023
142c479
feat: added new endpoint for check current user info
benyaar Feb 7, 2023
01d8a18
feature: CORS was added
YuryHouse Feb 7, 2023
af19649
feature: CORS was added
YuryHouse Feb 13, 2023
f137296
fix: question.service.ts
YuryHouse Feb 15, 2023
526fa8c
fix: question.controller.ts
YuryHouse Feb 19, 2023
6e4d10e
features: Created a Quiz-creation-date.js migration file with automat…
YuryHouse Feb 19, 2023
4dba03b
feat: added relation quiz-topic and created migration
benyaar Feb 20, 2023
684deee
fix: change allowNull in migration quiz-topic
benyaar Feb 21, 2023
6a39960
fix: added check for column existence
benyaar Feb 21, 2023
d32a1b7
fix: migration - Quiz-comment (added logic, info into model and dto)
YuryHouse Feb 22, 2023
c576b9d
fix: migration - Quiz-comment (added logic)
YuryHouse Feb 22, 2023
2a78122
fix: migration - Quiz-creationDate (added logic & quiz model improved)
YuryHouse Feb 22, 2023
91d5ecb
fix: migration - Moderation (added logic)
YuryHouse Feb 22, 2023
706d260
fix: .gitignore & added: postgresData
YuryHouse Feb 24, 2023
ebf6fc3
fix: CORS Configuration Options were added
YuryHouse Feb 28, 2023
e54de34
fix: CORS URL was added
YuryHouse Feb 28, 2023
6845191
fix: fixed update token
benyaar Mar 1, 2023
3842615
fix: was fixed property allowNull in quiz.model.ts
YuryHouse Mar 23, 2023
44dccac
feat(cc.api): were add logic for creating and add question to quiz
Mar 24, 2023
f4aa913
fix: was added emty string
YuryHouse Mar 26, 2023
e51e671
Merge remote-tracking branch 'origin/dev' into dev
YuryHouse Mar 27, 2023
198238f
feature: were prepared components for google-auth
YuryHouse Apr 11, 2023
83ef460
fix: were prepared components for google-auth
YuryHouse Apr 11, 2023
78a5c82
feature: were created google-auth login & google-auth redirect
YuryHouse Apr 12, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
57 changes: 36 additions & 21 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -46,27 +46,42 @@ services:
volumes:
- pgadmin:/root/.pgadmin

# chat-service:
# container_name: chat-service
# build: ./src/chat-service
# env_file:
# - ./src/chat-service/.env
# volumes:
# - ./src/chat-service:/chat
# - /chat/node_modules
# ports:
# - ${CHAT_SERVICE_PORT}:${CHAT_SERVICE_PORT}
# restart: always
#
# rmq:
# container_name: rmq
# image: rabbitmq:3-management
# env_file:
# - .env
# ports:
# - ${RABBITMQ_LOCAL_PORT}:${RABBITMQ_NATIVE_PORT}
# - "5672:5672"
# restart: always
chat-service:
container_name: chat-service
build: ./src/chat-service
env_file:
- ./src/chat-service/.env
volumes:
- ./src/chat-service:/chat
- /chat/node_modules
ports:
- ${CHAT_SERVICE_PORT}:${CHAT_SERVICE_PORT}
depends_on:
- postgres-chat
restart: always

postgres-chat:
container_name: postgres_chat
image: postgres:15
env_file:
- ./src/chat-service/.env
environment:
PG_DATA: /var/lib/postgresql/data
ports:
- ${CHAT_POSTGRES_PORT}:${POSTGRES_PORT}
volumes:
- pgdata-chat:/var/lib/postgresql/data
restart: always

rmq:
container_name: rmq
image: rabbitmq:3-management
env_file:
- .env
ports:
- ${RABBITMQ_LOCAL_PORT}:${RABBITMQ_NATIVE_PORT}
- '5672:5672'
restart: always

volumes:
pgdata:
Expand Down
4 changes: 4 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
"@nestjs/config": "^2.2.0",
"@nestjs/core": "^9.0.0",
"@nestjs/jwt": "^10.0.1",
"@nestjs/passport": "^9.0.3",
"@nestjs/platform-express": "^9.0.0",
"@nestjs/platform-socket.io": "^9.2.1",
"@nestjs/sequelize": "^9.0.0",
Expand All @@ -44,6 +45,8 @@
"cookie-parser": "^1.4.6",
"cross-env": "^7.0.3",
"dotenv": "^16.0.3",
"passport": "^0.6.0",
"passport-google-oauth20": "^2.0.0",
"pg": "^8.8.0",
"pg-hstore": "^2.3.4",
"reflect-metadata": "^0.1.13",
Expand All @@ -63,6 +66,7 @@
"@types/express": "^4.17.13",
"@types/jest": "28.1.8",
"@types/node": "^16.0.0",
"@types/passport-google-oauth20": "^2.0.11",
"@types/sequelize": "^4.28.14",
"@types/supertest": "^2.0.11",
"@typescript-eslint/eslint-plugin": "^5.0.0",
Expand Down
10 changes: 10 additions & 0 deletions src/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,11 @@ import { UserAccess } from './user/user.access.model';
import { UserModule } from './user/user.module';
import { AuthModule } from './auth/auth.module';
import { InventoryModule } from './inventory/inventory.module';
import { FeatureModule } from './feature/feature.module';
import { FeatureFlagModule } from './feature-flag/feature-flag.module';
import { FeatureFlag } from './feature-flag/feature-flag.model';
import { Feature } from './feature/feature.model';
import { GoogleUser } from './auth/oauth2/google/google.model';

@Module({
imports: [
Expand All @@ -47,6 +52,9 @@ import { InventoryModule } from './inventory/inventory.module';
Question,
Quiz_Question,
UserAccess,
Feature,
FeatureFlag,
GoogleUser,
],
autoLoadModels: true,
}),
Expand All @@ -59,6 +67,8 @@ import { InventoryModule } from './inventory/inventory.module';
ModerationModule,
QuestionsModule,
InventoryModule,
FeatureModule,
FeatureFlagModule,
],

controllers: [AppController],
Expand Down
13 changes: 13 additions & 0 deletions src/auth/auth.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import { Cookies } from './decorators/cookies.decorator';
import { JwtAuthGuard } from './jwt-auth.guard';
import { UserInReq } from './decorators/users.decorator';
import { UserReqDto } from './dto/user-req.dto';
import { GoogleAuthGuard } from './oauth2/google/google-auth.guard';

@ApiTags('Authorization')
@Controller('/api/auth')
Expand Down Expand Up @@ -124,4 +125,16 @@ export class AuthController {
async checkCurrentUser(@UserInReq() user: UserReqDto) {
return this.authService.checkCurrentUser(user.userId);
}

@Get('google/login')
@UseGuards(GoogleAuthGuard)
handleLogin() {
return { msg: 'Google Authentication' };
}

@Get('google/redirect')
@UseGuards(GoogleAuthGuard)
handleRedirect() {
return { msg: 'OK' };
}
}
15 changes: 14 additions & 1 deletion src/auth/auth.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,14 @@ import { UserModule } from '../user/user.module';
import { JwtModule } from '@nestjs/jwt';
import { AccessGroupModule } from '../access-group/access-group.module';
import { PermissionModule } from '../permission/permission.module';
import { GoogleStrategy } from './oauth2/google/google.strategy';
import { SessionSerializer } from './oauth2/google/serializer';
import { SequelizeModule } from '@nestjs/sequelize';
import { GoogleUser } from './oauth2/google/google.model';

@Module({
imports: [
SequelizeModule.forFeature([GoogleUser]),
UserModule,
AccessGroupModule,
PermissionModule,
Expand All @@ -16,7 +21,15 @@ import { PermissionModule } from '../permission/permission.module';
}),
],
controllers: [AuthController],
providers: [AuthService],
providers: [
AuthService,
GoogleStrategy,
SessionSerializer,
{
provide: 'GOOGLE_AUTH_SERVICE',
useClass: AuthService,
},
],
exports: [AuthService, JwtModule],
})
export class AuthModule {}
20 changes: 20 additions & 0 deletions src/auth/auth.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,15 @@ import { CreateUserDto } from '../user/dto/create-user.dto';
import { LoginUserDto, UserViewType } from './dto/login-user.dto';
import { JwtService } from '@nestjs/jwt';
import * as bcrypt from 'bcrypt';
import { GoogleLoginUserDto } from './oauth2/google/dto/google-login-user.dto';
import { InjectModel } from '@nestjs/sequelize';
import { GoogleUser } from './oauth2/google/google.model';

@Injectable()
export class AuthService {
constructor(
@InjectModel(GoogleUser)
private readonly googleUserModel: typeof GoogleUser,
private userService: UserService,
private jwtService: JwtService,
) {}
Expand Down Expand Up @@ -100,4 +105,19 @@ export class AuthService {
};
return userInfo;
}

async validateGoogleUser(details: GoogleLoginUserDto) {
console.log('AuthService');
console.log(details);
const googleUser = await this.googleUserModel.findOne({
where: { email: details.email },
});
console.log(googleUser);
if (googleUser) return googleUser;
console.log('User not found. Creating...');
return this.googleUserModel.create(details);
}
async findGoogleUser(id: number) {
return await this.googleUserModel.findOne({ where: { id } });
}
}
6 changes: 6 additions & 0 deletions src/auth/oauth2/google/dto/google-login-user.dto.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
export class GoogleLoginUserDto {
readonly googleId: string;
readonly email: string;
readonly displayName: string;
readonly photo: string;
}
12 changes: 12 additions & 0 deletions src/auth/oauth2/google/google-auth.guard.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { AuthGuard } from '@nestjs/passport';
import { ExecutionContext, Injectable } from '@nestjs/common';

@Injectable()
export class GoogleAuthGuard extends AuthGuard('google') {
async canActive(context: ExecutionContext) {
const activate = (await super.canActivate(context)) as boolean;
const request = context.switchToHttp().getRequest();
await super.logIn(request);
return activate;
}
}
42 changes: 42 additions & 0 deletions src/auth/oauth2/google/google.model.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import { Column, DataType, Model, Table } from 'sequelize-typescript';

interface GoogleUserAttrs {
googleId: string;
email: string;
displayName: string;
photo: string;
}
@Table({ tableName: 'google-user', createdAt: false, updatedAt: false })
export class GoogleUser extends Model<GoogleUser, GoogleUserAttrs> {
@Column({
type: DataType.INTEGER,
unique: true,
autoIncrement: true,
primaryKey: true,
})
id: number;

@Column({
type: DataType.STRING,
unique: true,
primaryKey: true,
})
googleId: string;

@Column({
type: DataType.STRING,
unique: true,
})
email: string;

@Column({
type: DataType.STRING,
})
displayName: string;

@Column({
type: DataType.STRING,
unique: true,
})
photo: string;
}
31 changes: 31 additions & 0 deletions src/auth/oauth2/google/google.strategy.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { PassportStrategy } from '@nestjs/passport';
import { Profile, Strategy } from 'passport-google-oauth20';
import { Inject, Injectable } from '@nestjs/common';
import * as process from 'process';
import { AuthService } from '../../auth.service';

@Injectable()
export class GoogleStrategy extends PassportStrategy(Strategy) {
constructor(
@Inject('GOOGLE_AUTH_SERVICE') private readonly authService: AuthService,
) {
super({
clientID: process.env.GOOGLE_CLIENT_ID,
clientSecret: process.env.GOOGLE_CLIENT_SECRET,
callbackURL: process.env.GOOGLE_CALLBACK_URL,
scope: ['profile', 'email'],
});
}
async validate(accessToken: string, refreshToken: string, profile: Profile) {
console.log(accessToken);
console.log(refreshToken);
console.log(profile);
const googleUser = await this.authService.validateGoogleUser({
email: profile.emails[0].value,
displayName: profile.displayName,
photo: profile.photos[0].value,
googleId: profile.id,
});
return googleUser || null;
}
}
27 changes: 27 additions & 0 deletions src/auth/oauth2/google/serializer.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { PassportSerializer } from '@nestjs/passport';
import { Inject, Injectable } from '@nestjs/common';
import { AuthService } from '../../auth.service';
import { GoogleUser } from './google.model';

@Injectable()
export class SessionSerializer extends PassportSerializer {
constructor(
@Inject('GOOGLE_AUTH_SERVICE') private readonly authService: AuthService,
) {
super();
}

// eslint-disable-next-line @typescript-eslint/ban-types
serializeUser(user: GoogleUser, done: Function) {
console.log('Deserialize User');
done(null, user);
}

// eslint-disable-next-line @typescript-eslint/ban-types
async deserializeUser(payload: any, done: Function) {
const googleUser = await this.authService.findGoogleUser(payload.id);
console.log('Deserialize User');
console.log(googleUser);
return googleUser ? done(null, googleUser) : done(null, null);
}
}
6 changes: 6 additions & 0 deletions src/feature-flag/dto/create-feature-flag.dto.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
export class CreateFeatureFlagDto {
readonly featureId: number;
readonly nextFeatureStatus: boolean;
readonly angularFeatureStatus: boolean;
readonly mobileFeatureStatus: boolean;
}
4 changes: 4 additions & 0 deletions src/feature-flag/feature-flag.controller.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import { Controller } from '@nestjs/common';

@Controller('feature-flag')
export class FeatureFlagController {}
40 changes: 40 additions & 0 deletions src/feature-flag/feature-flag.model.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import {
BelongsTo,
Column,
DataType,
ForeignKey,
Model,
Table,
} from 'sequelize-typescript';
import { Feature } from '../feature/feature.model';

export interface FeatureFlagCreationAttrs {
nextFeatureStatus: boolean;
angularFeatureStatus: boolean;
mobileFeatureStatus: boolean;
}

@Table({ tableName: 'feature-flags' })
export class FeatureFlag extends Model<FeatureFlag, FeatureFlagCreationAttrs> {
@Column({
type: DataType.INTEGER,
unique: true,
autoIncrement: true,
primaryKey: true,
})
id: number;

@ForeignKey(() => Feature)
@Column({ type: DataType.INTEGER })
featureId: number;

@Column({ type: DataType.BOOLEAN, allowNull: false })
nextFeatureStatus: boolean;
@Column({ type: DataType.BOOLEAN, allowNull: false })
angularFeatureStatus: boolean;
@Column({ type: DataType.BOOLEAN, allowNull: false })
mobileFeatureStatus: boolean;

@BelongsTo(() => Feature)
feature: Feature;
}
Loading