From 2ee97f8c8668a1c343ab8caa6d1e3920778b1289 Mon Sep 17 00:00:00 2001 From: liosha84 <138026690+liosha84@users.noreply.github.com> Date: Sun, 6 Jul 2025 19:37:32 +0300 Subject: [PATCH] Refactor tutorial service for public access and enhance authentication handling --- jambotron-ui/src/app/app.component.html | 55 -------- jambotron-ui/src/app/app.component.scss | 23 ---- jambotron-ui/src/app/app.component.spec.ts | 35 ----- jambotron-ui/src/app/app.component.ts | 124 ------------------ jambotron-ui/src/app/app.ts | 4 +- .../add-tutorial/add-tutorial.component.ts | 4 +- .../tutorial-details.component.ts | 16 +-- .../tutorials-list.component.ts | 8 +- .../tutorials.component.ts | 18 +-- .../src/app/helpers/auth.interceptor.ts | 44 +++++-- .../main.component/main.component.html | 19 +-- .../main.component/main.component.ts | 10 +- .../src/app/services/tutorial.service.ts | 46 +++---- .../tutorial-add.component.scss | 0 .../tutorial-add.component.spec.ts | 23 ++++ .../tutorial-add.component.ts | 11 ++ .../tutorials-list.component.html | 3 + .../tutorials-list.component.ts | 54 +++++++- .../src/app/user-module/user-api.service.ts | 2 +- .../user.component/user.component.ts | 4 +- .../src/app/user-module/user.routing.ts | 4 + .../jambotron/ZhiPuAi/ImageController.java | 6 +- .../controllers/TutorialController.java | 18 +-- .../jambotron/security/WebSecurityConfig.java | 10 +- .../security/jwt/AuthEntryPointJwt.java | 1 + 25 files changed, 217 insertions(+), 325 deletions(-) delete mode 100644 jambotron-ui/src/app/app.component.html delete mode 100644 jambotron-ui/src/app/app.component.scss delete mode 100644 jambotron-ui/src/app/app.component.spec.ts delete mode 100644 jambotron-ui/src/app/app.component.ts create mode 100644 jambotron-ui/src/app/user-module/tutorial-add.component/tutorial-add.component.scss create mode 100644 jambotron-ui/src/app/user-module/tutorial-add.component/tutorial-add.component.spec.ts create mode 100644 jambotron-ui/src/app/user-module/tutorial-add.component/tutorial-add.component.ts diff --git a/jambotron-ui/src/app/app.component.html b/jambotron-ui/src/app/app.component.html deleted file mode 100644 index 85204c4..0000000 --- a/jambotron-ui/src/app/app.component.html +++ /dev/null @@ -1,55 +0,0 @@ - - - - - - - - @if (showAdminBoard) { - - } - - @if (showAdminBoard) { - - } - - - - - - - - - - - - - -
- -
- diff --git a/jambotron-ui/src/app/app.component.scss b/jambotron-ui/src/app/app.component.scss deleted file mode 100644 index fee240c..0000000 --- a/jambotron-ui/src/app/app.component.scss +++ /dev/null @@ -1,23 +0,0 @@ -//#header_panel{ -// background-color: #181d1f; -//} -.example-spacer { - flex: 1 1 auto; -} - -mat-toolbar{ - background-color: rgba(153,153,153,0.16); - backdrop-filter: blur(8px); -} - -.fixed-top { - position: fixed; - top: 0; - right: 0; - left: 0; - z-index: 1000; // Ensure toolbar stays above other content -} -.router_outlet_padding{ - padding-top: 60px; - scroll-padding-inline: 40%; -} diff --git a/jambotron-ui/src/app/app.component.spec.ts b/jambotron-ui/src/app/app.component.spec.ts deleted file mode 100644 index 98d898f..0000000 --- a/jambotron-ui/src/app/app.component.spec.ts +++ /dev/null @@ -1,35 +0,0 @@ -import { TestBed } from '@angular/core/testing'; -import { RouterTestingModule } from '@angular/router/testing'; -import { AppComponent } from './app.component'; - -describe('AppComponent', () => { - beforeEach(async () => { - await TestBed.configureTestingModule({ - imports: [ - RouterTestingModule - ], - declarations: [ - AppComponent - ], - }).compileComponents(); - }); - - it('should create the app', () => { - const fixture = TestBed.createComponent(AppComponent); - const app = fixture.componentInstance; - expect(app).toBeTruthy(); - }); - - // it(`should have as title 'spring-angular-ui'`, () => { - // const fixture = TestBed.createComponent(AppComponent); - // const app = fixture.componentInstance; - // expect(app.title).toEqual('spring-angular-ui'); - // }); - - it('should render title', () => { - const fixture = TestBed.createComponent(AppComponent); - fixture.detectChanges(); - const compiled = fixture.nativeElement; - expect(compiled.querySelector('.content span').textContent).toContain('spring-angular-ui app is running!'); - }); -}); diff --git a/jambotron-ui/src/app/app.component.ts b/jambotron-ui/src/app/app.component.ts deleted file mode 100644 index b51a698..0000000 --- a/jambotron-ui/src/app/app.component.ts +++ /dev/null @@ -1,124 +0,0 @@ -import {Component, CUSTOM_ELEMENTS_SCHEMA, inject, OnInit} from '@angular/core'; -import { TokenStorageService } from './services/token-storage.service'; -import {MatDialog} from "@angular/material/dialog"; -import {DialogComponent} from "./components/dialog/dialog.component"; -import {AuthService} from "./services/auth.service"; -import {Subscription} from "rxjs"; -import {EventBusService} from "./_shared/event-bus.service"; -import {RouterLink, RouterOutlet} from '@angular/router'; -import {MatButton} from '@angular/material/button'; -import {MatToolbar} from '@angular/material/toolbar'; -import {MatIcon} from '@angular/material/icon'; - -@Component({ - selector: 'app-root', - templateUrl: './app.component.html', - schemas: [CUSTOM_ELEMENTS_SCHEMA], - imports: [ - RouterOutlet, - MatIcon, - MatButton, - RouterLink, - MatToolbar - ], - styleUrls: ['./app.component.scss'] -}) -export class AppComponent implements OnInit { - readonly dialog = inject(MatDialog); - //private authService = new AuthService(provideHttpClient()) - public dialogData : DialogData = {password: "", username: ""}; - - private roles: string[] = []; - isLoggedIn = false; - showAdminBoard = false; - showModeratorBoard = false; - username?: string; - - eventBusSub?: Subscription; - private errorMessage: any; - private isLoginFailed: boolean = false; - - constructor( - private storageService: TokenStorageService, - private authService: AuthService, - private eventBusService: EventBusService - ) {} - - ngOnInit(): void { - this.isLoggedIn = this.storageService.isLoggedIn(); - - if (this.isLoggedIn) { - const user = this.storageService.getUser(); - this.roles = user.roles; - - this.showAdminBoard = true;//this.roles.includes('ROLE_ADMIN'); - this.showModeratorBoard = this.roles.includes('ROLE_MODERATOR'); - - this.username = user.username; - } - - this.eventBusSub = this.eventBusService.on('logout', () => { - this.logout(); - }); - } - - logout(): void { - this.authService.logout().subscribe({ - next: res => { - console.log(res); - this.storageService.clean(); - - window.location.reload(); - }, - error: err => { - console.log(err); - } - }); - } - - openDialog(enterAnimationDuration: string, exitAnimationDuration: string) { - - let dialogRef = this.dialog.open(DialogComponent, { - width: '350px', - enterAnimationDuration, - exitAnimationDuration, - data: {username: this.dialogData.username, password: this.dialogData.password} - }); - - dialogRef.afterClosed().subscribe(result => { - console.log('The dialog was closed'); - if(result== null){ - return; - } - this.dialogData = result; - - this.authService.login(this.dialogData.username, this.dialogData.password).subscribe( - data => { - this.storageService.saveToken(data.accessToken); - this.storageService.saveUser(data); - - this.isLoginFailed = false; - this.isLoggedIn = true; - let user = this.storageService.getUser(); - this.roles = user.roles; - //this.username = user.username; - this.reloadPage(); - }, - err => { - this.errorMessage = err.error.message; - this.isLoginFailed = true; - } - ); - }); - - } - reloadPage(): void { - window.location.reload(); - } - -} -export interface DialogData { - username: string; - password: string; -} - diff --git a/jambotron-ui/src/app/app.ts b/jambotron-ui/src/app/app.ts index 649c4a1..23348c4 100644 --- a/jambotron-ui/src/app/app.ts +++ b/jambotron-ui/src/app/app.ts @@ -12,5 +12,7 @@ import { RouterOutlet } from '@angular/router'; export class App { protected title = 'jambotron-ui'; - constructor() { } + constructor() { + + } } diff --git a/jambotron-ui/src/app/components/add-tutorial/add-tutorial.component.ts b/jambotron-ui/src/app/components/add-tutorial/add-tutorial.component.ts index 3fe0470..be8a111 100644 --- a/jambotron-ui/src/app/components/add-tutorial/add-tutorial.component.ts +++ b/jambotron-ui/src/app/components/add-tutorial/add-tutorial.component.ts @@ -74,7 +74,7 @@ const language = 'typescript'; description: this.tutorial.description }; - this.tutorialService.create(data) +/* this.tutorialService.create(data) .subscribe( response => { console.log(response); @@ -82,7 +82,7 @@ const language = 'typescript'; }, error => { console.log(error); - }); + });*/ } newTutorial(): void { diff --git a/jambotron-ui/src/app/components/tutorial-details/tutorial-details.component.ts b/jambotron-ui/src/app/components/tutorial-details/tutorial-details.component.ts index 96a88d3..3991df1 100644 --- a/jambotron-ui/src/app/components/tutorial-details/tutorial-details.component.ts +++ b/jambotron-ui/src/app/components/tutorial-details/tutorial-details.component.ts @@ -36,7 +36,7 @@ export class TutorialDetailsComponent implements OnInit { } getTutorial(id: string): void { - this.tutorialService.get(id) +/* this.tutorialService.get(id) .subscribe( data => { this.currentTutorial = data; @@ -44,7 +44,7 @@ export class TutorialDetailsComponent implements OnInit { }, error => { console.log(error); - }); + });*/ } updatePublished(status: boolean): void { @@ -56,7 +56,7 @@ export class TutorialDetailsComponent implements OnInit { this.message = ''; - this.tutorialService.update(this.currentTutorial.id, data) +/* this.tutorialService.update(this.currentTutorial.id, data) .subscribe( response => { this.currentTutorial.published = status; @@ -65,11 +65,11 @@ export class TutorialDetailsComponent implements OnInit { }, error => { console.log(error); - }); + });*/ } updateTutorial(): void { - this.message = ''; + /* this.message = ''; this.tutorialService.update(this.currentTutorial.id, this.currentTutorial) .subscribe( @@ -79,11 +79,11 @@ export class TutorialDetailsComponent implements OnInit { }, error => { console.log(error); - }); + });*/ } deleteTutorial(): void { - this.tutorialService.delete(this.currentTutorial.id) + /* this.tutorialService.delete(this.currentTutorial.id) .subscribe( response => { console.log(response); @@ -91,6 +91,6 @@ export class TutorialDetailsComponent implements OnInit { }, error => { console.log(error); - }); + });*/ } } diff --git a/jambotron-ui/src/app/components/tutorials-list/tutorials-list.component.ts b/jambotron-ui/src/app/components/tutorials-list/tutorials-list.component.ts index f1cdb3f..0442b48 100644 --- a/jambotron-ui/src/app/components/tutorials-list/tutorials-list.component.ts +++ b/jambotron-ui/src/app/components/tutorials-list/tutorials-list.component.ts @@ -31,9 +31,9 @@ export class TutorialsListComponent implements OnInit { } retrieveTutorials(): void { - this.tutorialService.getAll() + this.tutorialService.getAllPublic() .subscribe( - data => { + (data: Tutorial[] ) => { this.tutorials = data; console.log(data); }, @@ -54,7 +54,7 @@ export class TutorialsListComponent implements OnInit { } removeAllTutorials(): void { - this.tutorialService.deleteAll() + /* this.tutorialService.deleteAll() .subscribe( response => { console.log(response); @@ -62,7 +62,7 @@ export class TutorialsListComponent implements OnInit { }, error => { console.log(error); - }); + });*/ } searchTitle(): void { diff --git a/jambotron-ui/src/app/components/tutorials.component/tutorials.component.ts b/jambotron-ui/src/app/components/tutorials.component/tutorials.component.ts index 74325b8..d539f0d 100644 --- a/jambotron-ui/src/app/components/tutorials.component/tutorials.component.ts +++ b/jambotron-ui/src/app/components/tutorials.component/tutorials.component.ts @@ -36,14 +36,14 @@ export class TutorialsComponent { // } retrieveTutorials(): void { - this.tutorialService.getAll() - .subscribe( - data => { - this.tutorials = data; - console.log(data); - }, - error => { - console.log(error); - }); + this.tutorialService.getAllPublic().subscribe( + (data : Tutorial[]) =>{ + this.tutorials = data; + console.log(data); + }, + error => { + console.log(error); + } + ); } } diff --git a/jambotron-ui/src/app/helpers/auth.interceptor.ts b/jambotron-ui/src/app/helpers/auth.interceptor.ts index 6d52bee..390b93c 100644 --- a/jambotron-ui/src/app/helpers/auth.interceptor.ts +++ b/jambotron-ui/src/app/helpers/auth.interceptor.ts @@ -1,33 +1,57 @@ -import { HTTP_INTERCEPTORS, HttpEvent } from '@angular/common/http'; +import {HTTP_INTERCEPTORS, HttpErrorResponse, HttpEvent} from '@angular/common/http'; import { Injectable } from '@angular/core'; import { HttpInterceptor, HttpHandler, HttpRequest } from '@angular/common/http'; import { TokenStorageService } from '../services/token-storage.service'; -import { Observable } from 'rxjs'; +import {catchError, Observable, throwError} from 'rxjs'; +import {EventBusService} from '../_shared/event-bus.service'; +import {EventData} from '../_shared/event.class'; +import {environment} from '../../environments/environment'; const TOKEN_HEADER_KEY = 'Authorization'; // for Spring Boot back-end @Injectable() export class AuthInterceptor implements HttpInterceptor { - constructor(private token: TokenStorageService) { } + private isRefreshing = false; + enviorment = environment; + + constructor(private tokenStorageService: TokenStorageService, private eventBusService: EventBusService) { } intercept(req: HttpRequest, next: HttpHandler): Observable> { - req = req.clone({ withCredentials: true, }); - return next.handle(req); + return next.handle(req).pipe( + catchError((error) => { + if ( + error instanceof HttpErrorResponse && + !req.url.includes('auth/signin') && + (error.status === 401 + || error.status === 500 + || error.status === 0 + ) /// must be only 401 without 500 and 0 + ) { + return this.handle401Error(req, next); + } - /*let authReq = req; - const token = this.token.getToken(); - if (token != null) { - authReq = req.clone({ headers: req.headers.set(TOKEN_HEADER_KEY, 'Bearer ' + token) }); + return throwError(() => error); + }) + ); + } + + private handle401Error(request: HttpRequest, next: HttpHandler) { + if (!this.isRefreshing) { + this.isRefreshing = true; + + if (this.tokenStorageService.isLoggedIn()) { + this.eventBusService.emit(new EventData('logout', null)); + } } - return next.handle(authReq);*/ + return next.handle(request); } } diff --git a/jambotron-ui/src/app/main-module/main.component/main.component.html b/jambotron-ui/src/app/main-module/main.component/main.component.html index 19894b2..3f83962 100644 --- a/jambotron-ui/src/app/main-module/main.component/main.component.html +++ b/jambotron-ui/src/app/main-module/main.component/main.component.html @@ -27,16 +27,19 @@ @if (showAdminBoard) { - + + } + + @if (showUserBoard) { + } - + @for (tutorial of tutorials; track tutorial) {