Skip to content

Commit

Permalink
login page
Browse files Browse the repository at this point in the history
  • Loading branch information
Unischneider committed Nov 6, 2024
1 parent 3871b35 commit e6a4c29
Show file tree
Hide file tree
Showing 10 changed files with 209 additions and 23 deletions.
15 changes: 15 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
"@angular/platform-browser-dynamic": "^18.2.0",
"@angular/router": "^18.2.0",
"@ng-select/ng-select": "^13.9.1",
"crypto-js": "^4.2.0",
"rxjs": "~7.8.0",
"tslib": "^2.3.0",
"zone.js": "~0.14.10"
Expand All @@ -27,6 +28,7 @@
"@angular-devkit/build-angular": "^18.2.7",
"@angular/cli": "^18.2.7",
"@angular/compiler-cli": "^18.2.0",
"@types/crypto-js": "^4.2.2",
"@types/jasmine": "~5.1.0",
"jasmine-core": "~5.2.0",
"karma": "~6.4.0",
Expand All @@ -36,4 +38,4 @@
"karma-jasmine-html-reporter": "~2.1.0",
"typescript": "~5.5.2"
}
}
}
10 changes: 7 additions & 3 deletions src/app/app.routes.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
import { Routes } from '@angular/router';
import { ChatComponent } from './chat/chat.component';
import { LoginComponent } from './login/login.component';
import { AuthGuard } from './utils/auth.guard';

export const routes: Routes = [
{ path: 'chat/en', component: ChatComponent, data: { language: 'en' } },
{ path: 'chat/de', component: ChatComponent, data: { language: 'de' } },
{ path: '', redirectTo: 'chat/en', pathMatch: 'full' } // Default to English chat
{ path: 'login', component: LoginComponent },
{ path: 'chat/en', component: ChatComponent, canActivate: [AuthGuard], data: { language: 'en' } },
{ path: 'chat/de', component: ChatComponent, canActivate: [AuthGuard], data: { language: 'de' } },
{ path: '', redirectTo: 'login', pathMatch: 'full' },
{ path: '**', redirectTo: 'login', pathMatch: 'full' }
];
28 changes: 28 additions & 0 deletions src/app/login/login.component.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<div class="login-container">
<h2>Login</h2>
<form [formGroup]="loginForm" (ngSubmit)="onSubmit()">
<div class="form-group">
<label for="username">Username</label>
<input id="username" type="text" formControlName="username"
[class.invalid]="loginForm.get('username')?.invalid && loginForm.get('username')?.touched" />
<div *ngIf="loginForm.get('username')?.invalid && loginForm.get('username')?.touched" class="error">
Username is required
</div>
</div>

<div class="form-group">
<label for="password">Password</label>
<input id="password" type="password" formControlName="password"
[class.invalid]="loginForm.get('password')?.invalid && loginForm.get('password')?.touched" />
<div *ngIf="loginForm.get('password')?.invalid && loginForm.get('password')?.touched" class="error">
Password is required
</div>
</div>

<div *ngIf="errorMessage" class="error">
{{ errorMessage }}
</div>

<button type="submit" [disabled]="loginForm.invalid">Login</button>
</form>
</div>
53 changes: 53 additions & 0 deletions src/app/login/login.component.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
.login-container {
width: 300px;
margin: 0 auto;
padding: 2rem;
border: 1px solid #ddd;
border-radius: 8px;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
}

h2 {
text-align: center;
}

.form-group {
margin-bottom: 1rem;
}

label {
display: block;
font-weight: bold;
margin-bottom: 0.5rem;
}

input {
width: 100%;
padding: 0.5rem;
border: 1px solid #ccc;
border-radius: 4px;
}

button {
width: 100%;
padding: 0.7rem;
background-color: #007bff;
color: #fff;
border: none;
border-radius: 4px;
cursor: pointer;
}

button:disabled {
background-color: #a9a9a9;
cursor: not-allowed;
}

.error {
color: red;
font-size: 0.85rem;
}

.invalid {
border-color: red;
}
23 changes: 23 additions & 0 deletions src/app/login/login.component.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';

import { LoginComponent } from './login.component';

describe('LoginComponent', () => {
let component: LoginComponent;
let fixture: ComponentFixture<LoginComponent>;

beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [LoginComponent]
})
.compileComponents();

fixture = TestBed.createComponent(LoginComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});

it('should create', () => {
expect(component).toBeTruthy();
});
});
40 changes: 40 additions & 0 deletions src/app/login/login.component.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import { Component } from '@angular/core';
import { FormControl, FormGroup, ReactiveFormsModule, Validators } from '@angular/forms';
import { Router } from '@angular/router';
import { AuthService } from '../services/auth.service';

@Component({
selector: 'app-login',
standalone: true,
imports: [ReactiveFormsModule],
templateUrl: './login.component.html',
styleUrl: './login.component.scss'
})
export class LoginComponent {
public loginForm: FormGroup;
errorMessage: string | null = null;

constructor(
private authService: AuthService,
private router: Router
) {
this.loginForm = new FormGroup({
username: new FormControl<string>('', [Validators.required]),
password: new FormControl<string>('', [Validators.required])
});
}

onSubmit(): void {
if (this.loginForm.valid) {
const { username, password } = this.loginForm.value;
this.authService.login(username, password).subscribe({
next: () => this.router.navigate(['/chat/en']), // Redirect to chat after login
error: (err) => (this.errorMessage = 'Invalid username or password')
});
}
}

get f() {
return this.loginForm.controls;
}
}
29 changes: 17 additions & 12 deletions src/app/services/auth.service.ts
Original file line number Diff line number Diff line change
@@ -1,37 +1,42 @@
import { HttpClient, HttpHeaders } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { Observable, tap } from 'rxjs';
import { BehaviorSubject, Observable, tap } from 'rxjs';
import { environment } from '../../environments/environment';
import * as CryptoJS from 'crypto-js';

export interface AuthResponse {
access_token: string;
token_type: string;
}

@Injectable({
providedIn: 'root'
})
export class AuthService {
private isAuthenticated = new BehaviorSubject<boolean>(this.getToken() !== null);

constructor(private http: HttpClient) { }

login(): Observable<AuthResponse> {
login(username: string, password: string): Observable<AuthResponse> {
const headers = new HttpHeaders().set('x-api-key', environment.angelosAppApiKey);
if (environment.angelosAppApiKey.length === 0) {
console.log('Please provide a valid API key');
} else {
console.log(environment.angelosAppApiKey.at(0));
}

return this.http.post<AuthResponse>(environment.angelosToken, {}, { headers }).pipe(
const body = { username: username, password: password };
return this.http.post<AuthResponse>(environment.angelosToken, body, { headers }).pipe(
tap((response: AuthResponse) => {
sessionStorage.setItem('access_token', response.access_token);
this.isAuthenticated.next(true);
})
);
}

// Method to retrieve the stored token
public getToken(): string | null {
logout(): void {
sessionStorage.removeItem('access_token');
this.isAuthenticated.next(false);
}

getToken(): string | null {
return sessionStorage.getItem('access_token');
}

isLoggedIn(): Observable<boolean> {
return this.isAuthenticated.asObservable();
}
}
8 changes: 1 addition & 7 deletions src/app/services/chatbot.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,13 +25,7 @@ export class ChatbotService {
if (token) {
return this.sendBotRequest(token, chatHistory, study_program);
} else {
// Login if no token is stored, then proceed with the bot request
return this.authService.login().pipe(
switchMap(() => {
const newToken = this.authService.getToken();
return this.sendBotRequest(newToken, chatHistory, study_program);
})
);
throw new Error('No token found. Access should have been restricted by AuthGuard.');
}
}
}
22 changes: 22 additions & 0 deletions src/app/utils/auth.guard.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { Injectable } from '@angular/core';
import { CanActivate, Router } from '@angular/router';
import { Observable } from 'rxjs';
import { map, tap } from 'rxjs/operators';
import { AuthService } from '../services/auth.service';

@Injectable({
providedIn: 'root'
})
export class AuthGuard implements CanActivate {
constructor(private authService: AuthService, private router: Router) { }

canActivate(): Observable<boolean> {
return this.authService.isLoggedIn().pipe(
tap(isLoggedIn => {
if (!isLoggedIn) {
this.router.navigate(['/login']);
}
})
);
}
}

0 comments on commit e6a4c29

Please sign in to comment.