@@ -65,7 +65,13 @@
|
|||||||
"development": {
|
"development": {
|
||||||
"optimization": false,
|
"optimization": false,
|
||||||
"extractLicenses": false,
|
"extractLicenses": false,
|
||||||
"sourceMap": true
|
"sourceMap": true,
|
||||||
|
"fileReplacements": [
|
||||||
|
{
|
||||||
|
"replace": "src/environments/environment.ts",
|
||||||
|
"with": "src/environments/environment.development.ts"
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"defaultConfiguration": "production"
|
"defaultConfiguration": "production"
|
||||||
|
|||||||
@@ -1,55 +0,0 @@
|
|||||||
|
|
||||||
<mat-toolbar class="fixed-top">
|
|
||||||
<button mat-raised-button routerLink="/" href="#">
|
|
||||||
My App
|
|
||||||
</button>
|
|
||||||
<span class="example-spacer"></span>
|
|
||||||
<button mat-raised-button routerLink="tutorials">
|
|
||||||
Tutorials
|
|
||||||
</button>
|
|
||||||
<button mat-raised-button routerLink="add" *ngIf="isLoggedIn">
|
|
||||||
Add tutorial
|
|
||||||
</button>
|
|
||||||
<span class="example-spacer"></span>
|
|
||||||
@if (showAdminBoard) {
|
|
||||||
<button mat-raised-button routerLink="admin" >
|
|
||||||
<mat-icon>manage_accounts</mat-icon>
|
|
||||||
Admin Bord
|
|
||||||
</button>
|
|
||||||
}
|
|
||||||
|
|
||||||
@if (showAdminBoard) {
|
|
||||||
<button mat-raised-button routerLink="system" >
|
|
||||||
<mat-icon>settings_applications</mat-icon>
|
|
||||||
System
|
|
||||||
</button>
|
|
||||||
}
|
|
||||||
|
|
||||||
<span class="example-spacer"></span>
|
|
||||||
<button mat-raised-button routerLink="register" *ngIf="!isLoggedIn">
|
|
||||||
<mat-icon>app_registration</mat-icon>
|
|
||||||
Register
|
|
||||||
</button>
|
|
||||||
|
|
||||||
<button mat-raised-button (click)="openDialog('100ms', '5ms')" *ngIf="!isLoggedIn">
|
|
||||||
<mat-icon>login</mat-icon>
|
|
||||||
Login
|
|
||||||
</button>
|
|
||||||
|
|
||||||
<button mat-raised-button routerLink="profile" *ngIf="isLoggedIn">
|
|
||||||
<mat-icon>account_circle</mat-icon>
|
|
||||||
{{ username }}
|
|
||||||
</button>
|
|
||||||
|
|
||||||
<button mat-raised-button (click)="logout()"*ngIf="isLoggedIn">
|
|
||||||
<mat-icon>logout</mat-icon>
|
|
||||||
Logout
|
|
||||||
</button>
|
|
||||||
|
|
||||||
</mat-toolbar>
|
|
||||||
|
|
||||||
|
|
||||||
<div class="router_outlet_padding">
|
|
||||||
<router-outlet></router-outlet>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
@@ -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%;
|
|
||||||
}
|
|
||||||
@@ -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!');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@@ -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;
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -18,9 +18,11 @@ import 'prismjs';
|
|||||||
import 'prismjs/components/prism-typescript.min.js';
|
import 'prismjs/components/prism-typescript.min.js';
|
||||||
import 'prismjs/plugins/line-numbers/prism-line-numbers.js';
|
import 'prismjs/plugins/line-numbers/prism-line-numbers.js';
|
||||||
import 'prismjs/plugins/line-highlight/prism-line-highlight.js';
|
import 'prismjs/plugins/line-highlight/prism-line-highlight.js';
|
||||||
|
import {authInterceptorProviders} from './helpers/auth.interceptor';
|
||||||
|
|
||||||
export const appConfig: ApplicationConfig = {
|
export const appConfig: ApplicationConfig = {
|
||||||
providers: [
|
providers: [
|
||||||
|
authInterceptorProviders,
|
||||||
provideBrowserGlobalErrorListeners(),
|
provideBrowserGlobalErrorListeners(),
|
||||||
provideAnimations(),
|
provideAnimations(),
|
||||||
provideZoneChangeDetection({ eventCoalescing: true }),
|
provideZoneChangeDetection({ eventCoalescing: true }),
|
||||||
|
|||||||
@@ -12,5 +12,7 @@ import { RouterOutlet } from '@angular/router';
|
|||||||
export class App {
|
export class App {
|
||||||
protected title = 'jambotron-ui';
|
protected title = 'jambotron-ui';
|
||||||
|
|
||||||
constructor() { }
|
constructor() {
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -74,7 +74,7 @@ const language = 'typescript';
|
|||||||
description: this.tutorial.description
|
description: this.tutorial.description
|
||||||
};
|
};
|
||||||
|
|
||||||
this.tutorialService.create(data)
|
/* this.tutorialService.create(data)
|
||||||
.subscribe(
|
.subscribe(
|
||||||
response => {
|
response => {
|
||||||
console.log(response);
|
console.log(response);
|
||||||
@@ -82,7 +82,7 @@ const language = 'typescript';
|
|||||||
},
|
},
|
||||||
error => {
|
error => {
|
||||||
console.log(error);
|
console.log(error);
|
||||||
});
|
});*/
|
||||||
}
|
}
|
||||||
|
|
||||||
newTutorial(): void {
|
newTutorial(): void {
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ export class TutorialDetailsComponent implements OnInit {
|
|||||||
}
|
}
|
||||||
|
|
||||||
getTutorial(id: string): void {
|
getTutorial(id: string): void {
|
||||||
this.tutorialService.get(id)
|
/* this.tutorialService.get(id)
|
||||||
.subscribe(
|
.subscribe(
|
||||||
data => {
|
data => {
|
||||||
this.currentTutorial = data;
|
this.currentTutorial = data;
|
||||||
@@ -44,7 +44,7 @@ export class TutorialDetailsComponent implements OnInit {
|
|||||||
},
|
},
|
||||||
error => {
|
error => {
|
||||||
console.log(error);
|
console.log(error);
|
||||||
});
|
});*/
|
||||||
}
|
}
|
||||||
|
|
||||||
updatePublished(status: boolean): void {
|
updatePublished(status: boolean): void {
|
||||||
@@ -56,7 +56,7 @@ export class TutorialDetailsComponent implements OnInit {
|
|||||||
|
|
||||||
this.message = '';
|
this.message = '';
|
||||||
|
|
||||||
this.tutorialService.update(this.currentTutorial.id, data)
|
/* this.tutorialService.update(this.currentTutorial.id, data)
|
||||||
.subscribe(
|
.subscribe(
|
||||||
response => {
|
response => {
|
||||||
this.currentTutorial.published = status;
|
this.currentTutorial.published = status;
|
||||||
@@ -65,11 +65,11 @@ export class TutorialDetailsComponent implements OnInit {
|
|||||||
},
|
},
|
||||||
error => {
|
error => {
|
||||||
console.log(error);
|
console.log(error);
|
||||||
});
|
});*/
|
||||||
}
|
}
|
||||||
|
|
||||||
updateTutorial(): void {
|
updateTutorial(): void {
|
||||||
this.message = '';
|
/* this.message = '';
|
||||||
|
|
||||||
this.tutorialService.update(this.currentTutorial.id, this.currentTutorial)
|
this.tutorialService.update(this.currentTutorial.id, this.currentTutorial)
|
||||||
.subscribe(
|
.subscribe(
|
||||||
@@ -79,11 +79,11 @@ export class TutorialDetailsComponent implements OnInit {
|
|||||||
},
|
},
|
||||||
error => {
|
error => {
|
||||||
console.log(error);
|
console.log(error);
|
||||||
});
|
});*/
|
||||||
}
|
}
|
||||||
|
|
||||||
deleteTutorial(): void {
|
deleteTutorial(): void {
|
||||||
this.tutorialService.delete(this.currentTutorial.id)
|
/* this.tutorialService.delete(this.currentTutorial.id)
|
||||||
.subscribe(
|
.subscribe(
|
||||||
response => {
|
response => {
|
||||||
console.log(response);
|
console.log(response);
|
||||||
@@ -91,6 +91,6 @@ export class TutorialDetailsComponent implements OnInit {
|
|||||||
},
|
},
|
||||||
error => {
|
error => {
|
||||||
console.log(error);
|
console.log(error);
|
||||||
});
|
});*/
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -31,9 +31,9 @@ export class TutorialsListComponent implements OnInit {
|
|||||||
}
|
}
|
||||||
|
|
||||||
retrieveTutorials(): void {
|
retrieveTutorials(): void {
|
||||||
this.tutorialService.getAll()
|
this.tutorialService.getAllPublic()
|
||||||
.subscribe(
|
.subscribe(
|
||||||
data => {
|
(data: Tutorial[] ) => {
|
||||||
this.tutorials = data;
|
this.tutorials = data;
|
||||||
console.log(data);
|
console.log(data);
|
||||||
},
|
},
|
||||||
@@ -54,7 +54,7 @@ export class TutorialsListComponent implements OnInit {
|
|||||||
}
|
}
|
||||||
|
|
||||||
removeAllTutorials(): void {
|
removeAllTutorials(): void {
|
||||||
this.tutorialService.deleteAll()
|
/* this.tutorialService.deleteAll()
|
||||||
.subscribe(
|
.subscribe(
|
||||||
response => {
|
response => {
|
||||||
console.log(response);
|
console.log(response);
|
||||||
@@ -62,7 +62,7 @@ export class TutorialsListComponent implements OnInit {
|
|||||||
},
|
},
|
||||||
error => {
|
error => {
|
||||||
console.log(error);
|
console.log(error);
|
||||||
});
|
});*/
|
||||||
}
|
}
|
||||||
|
|
||||||
searchTitle(): void {
|
searchTitle(): void {
|
||||||
|
|||||||
@@ -3,6 +3,9 @@ import {Tutorial} from '../../models/tutorial.model';
|
|||||||
import {TutorialService} from '../../services/tutorial.service';
|
import {TutorialService} from '../../services/tutorial.service';
|
||||||
import {MatCard, MatCardContent, MatCardHeader} from '@angular/material/card';
|
import {MatCard, MatCardContent, MatCardHeader} from '@angular/material/card';
|
||||||
import {MarkdownComponent} from 'ngx-markdown';
|
import {MarkdownComponent} from 'ngx-markdown';
|
||||||
|
import {authInterceptorProviders} from '../../helpers/auth.interceptor';
|
||||||
|
import {HTTP_INTERCEPTORS, provideHttpClient, withInterceptorsFromDi} from '@angular/common/http';
|
||||||
|
import {CustomHttpInterceptor} from '../../helpers/custom-http-interceptor';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-tutorials.component',
|
selector: 'app-tutorials.component',
|
||||||
@@ -14,7 +17,12 @@ import {MarkdownComponent} from 'ngx-markdown';
|
|||||||
],
|
],
|
||||||
templateUrl: './tutorials.component.html',
|
templateUrl: './tutorials.component.html',
|
||||||
styleUrl: './tutorials.component.scss',
|
styleUrl: './tutorials.component.scss',
|
||||||
schemas: [CUSTOM_ELEMENTS_SCHEMA]
|
schemas: [CUSTOM_ELEMENTS_SCHEMA],
|
||||||
|
providers: [authInterceptorProviders,{
|
||||||
|
provide: HTTP_INTERCEPTORS,
|
||||||
|
useClass: CustomHttpInterceptor,
|
||||||
|
multi: true
|
||||||
|
}],
|
||||||
})
|
})
|
||||||
export class TutorialsComponent {
|
export class TutorialsComponent {
|
||||||
tutorials?: Tutorial[];
|
tutorials?: Tutorial[];
|
||||||
@@ -28,14 +36,14 @@ export class TutorialsComponent {
|
|||||||
// }
|
// }
|
||||||
|
|
||||||
retrieveTutorials(): void {
|
retrieveTutorials(): void {
|
||||||
this.tutorialService.getAll()
|
this.tutorialService.getAllPublic().subscribe(
|
||||||
.subscribe(
|
(data : Tutorial[]) =>{
|
||||||
data => {
|
|
||||||
this.tutorials = data;
|
this.tutorials = data;
|
||||||
console.log(data);
|
console.log(data);
|
||||||
},
|
},
|
||||||
error => {
|
error => {
|
||||||
console.log(error);
|
console.log(error);
|
||||||
});
|
}
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,23 +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 { Injectable } from '@angular/core';
|
||||||
import { HttpInterceptor, HttpHandler, HttpRequest } from '@angular/common/http';
|
import { HttpInterceptor, HttpHandler, HttpRequest } from '@angular/common/http';
|
||||||
|
|
||||||
import { TokenStorageService } from '../services/token-storage.service';
|
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
|
const TOKEN_HEADER_KEY = 'Authorization'; // for Spring Boot back-end
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class AuthInterceptor implements HttpInterceptor {
|
export class AuthInterceptor implements HttpInterceptor {
|
||||||
constructor(private token: TokenStorageService) { }
|
private isRefreshing = false;
|
||||||
|
enviorment = environment;
|
||||||
|
|
||||||
|
constructor(private tokenStorageService: TokenStorageService, private eventBusService: EventBusService) { }
|
||||||
|
|
||||||
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
|
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
|
||||||
let authReq = req;
|
|
||||||
const token = this.token.getToken();
|
req = req.clone({
|
||||||
if (token != null) {
|
withCredentials: true,
|
||||||
authReq = req.clone({ headers: req.headers.set(TOKEN_HEADER_KEY, 'Bearer ' + token) });
|
});
|
||||||
|
|
||||||
|
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);
|
||||||
}
|
}
|
||||||
return next.handle(authReq);
|
|
||||||
|
return throwError(() => error);
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private handle401Error(request: HttpRequest<any>, next: HttpHandler) {
|
||||||
|
if (!this.isRefreshing) {
|
||||||
|
this.isRefreshing = true;
|
||||||
|
|
||||||
|
if (this.tokenStorageService.isLoggedIn()) {
|
||||||
|
this.eventBusService.emit(new EventData('logout', null));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return next.handle(request);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -33,10 +33,13 @@
|
|||||||
</button>
|
</button>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@if (showUserBoard) {
|
||||||
<button mat-menu-item routerLink="user/user-welcome">
|
<button mat-menu-item routerLink="user/user-welcome">
|
||||||
<mat-icon>space_dashboard</mat-icon>
|
<mat-icon>space_dashboard</mat-icon>
|
||||||
User tools
|
User tools
|
||||||
</button>
|
</button>
|
||||||
|
}
|
||||||
|
|
||||||
<button mat-menu-item routerLink="profile">
|
<button mat-menu-item routerLink="profile">
|
||||||
<mat-icon>person</mat-icon>
|
<mat-icon>person</mat-icon>
|
||||||
Profile
|
Profile
|
||||||
|
|||||||
@@ -18,6 +18,9 @@ import {MatMenu, MatMenuItem, MatMenuTrigger} from '@angular/material/menu';
|
|||||||
import {MatTooltip} from '@angular/material/tooltip';
|
import {MatTooltip} from '@angular/material/tooltip';
|
||||||
import {DialogSignupComponent} from '../../components/dialog-signup.component/dialog-signup.component';
|
import {DialogSignupComponent} from '../../components/dialog-signup.component/dialog-signup.component';
|
||||||
import {MatLabel} from '@angular/material/input';
|
import {MatLabel} from '@angular/material/input';
|
||||||
|
import {HttpClient} from '@angular/common/http';
|
||||||
|
|
||||||
|
import { environment } from '../../../environments/environment';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-main.component',
|
selector: 'app-main.component',
|
||||||
@@ -58,24 +61,23 @@ export class MainComponent implements OnInit{
|
|||||||
isLoggedIn = false;
|
isLoggedIn = false;
|
||||||
showAdminBoard = false;
|
showAdminBoard = false;
|
||||||
showModeratorBoard = false;
|
showModeratorBoard = false;
|
||||||
|
showUserBoard = false;
|
||||||
username?: string;
|
username?: string;
|
||||||
|
|
||||||
eventBusSub?: Subscription;
|
eventBusSub?: Subscription;
|
||||||
|
|
||||||
|
private environment = environment;
|
||||||
|
|
||||||
private storageService: TokenStorageService = inject(TokenStorageService);
|
private storageService: TokenStorageService = inject(TokenStorageService);
|
||||||
private authService: AuthService = inject(AuthService);
|
private authService: AuthService = inject(AuthService);
|
||||||
private eventBusService: EventBusService = inject(EventBusService);
|
private eventBusService: EventBusService = inject(EventBusService);
|
||||||
|
|
||||||
constructor(private router: Router) {
|
constructor(private router: Router,private http: HttpClient) {
|
||||||
|
|
||||||
}
|
}
|
||||||
goToHome() {
|
|
||||||
this.router.navigate(['main/home']);
|
|
||||||
}
|
|
||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
this.isLoggedIn = this.storageService.isLoggedIn();
|
|
||||||
|
|
||||||
this.refreshToolbar();
|
this.refreshToolbar();
|
||||||
|
|
||||||
@@ -85,12 +87,14 @@ export class MainComponent implements OnInit{
|
|||||||
}
|
}
|
||||||
|
|
||||||
refreshToolbar() {
|
refreshToolbar() {
|
||||||
|
this.isLoggedIn = this.storageService.isLoggedIn();
|
||||||
if (this.isLoggedIn) {
|
if (this.isLoggedIn) {
|
||||||
const user = this.storageService.getUser();
|
const user = this.storageService.getUser();
|
||||||
this.roles = user.roles;
|
this.roles = user.roles;
|
||||||
|
|
||||||
this.showAdminBoard = true;//this.roles.includes('ROLE_ADMIN');
|
this.showAdminBoard = this.roles.includes('ROLE_ADMIN');
|
||||||
this.showModeratorBoard = this.roles.includes('ROLE_MODERATOR');
|
this.showModeratorBoard = this.roles.includes('ROLE_MODERATOR');
|
||||||
|
this.showUserBoard = this.roles.includes('ROLE_USER');
|
||||||
|
|
||||||
this.username = user.username;
|
this.username = user.username;
|
||||||
}
|
}
|
||||||
@@ -136,6 +140,8 @@ export class MainComponent implements OnInit{
|
|||||||
console.log(res);
|
console.log(res);
|
||||||
this.storageService.clean();
|
this.storageService.clean();
|
||||||
|
|
||||||
|
//window.location.reload();
|
||||||
|
this.router.navigate([environment.default_page]);
|
||||||
window.location.reload();
|
window.location.reload();
|
||||||
},
|
},
|
||||||
error: err => {
|
error: err => {
|
||||||
@@ -168,7 +174,8 @@ export class MainComponent implements OnInit{
|
|||||||
|
|
||||||
this.authService.login(this.dialogLoginData.username, this.dialogLoginData.password).subscribe(
|
this.authService.login(this.dialogLoginData.username, this.dialogLoginData.password).subscribe(
|
||||||
data => {
|
data => {
|
||||||
this.storageService.saveToken(data.accessToken);
|
|
||||||
|
//this.storageService.saveToken(data.token);
|
||||||
this.storageService.saveUser(data);
|
this.storageService.saveUser(data);
|
||||||
|
|
||||||
this.isLoggedIn = true;
|
this.isLoggedIn = true;
|
||||||
|
|||||||
@@ -4,11 +4,12 @@ import {mainRouting} from './main.routing';
|
|||||||
import {NgModule} from '@angular/core';
|
import {NgModule} from '@angular/core';
|
||||||
import {CommonModule} from '@angular/common';
|
import {CommonModule} from '@angular/common';
|
||||||
|
|
||||||
import {HTTP_INTERCEPTORS} from '@angular/common/http';
|
import {HTTP_INTERCEPTORS, provideHttpClient, withInterceptorsFromDi} from '@angular/common/http';
|
||||||
import {CustomHttpInterceptor} from '../helpers/custom-http-interceptor';
|
import {CustomHttpInterceptor} from '../helpers/custom-http-interceptor';
|
||||||
import {AngularImageViewerModule} from '@hreimer/angular-image-viewer';
|
import {AngularImageViewerModule} from '@hreimer/angular-image-viewer';
|
||||||
import {BrowserModule} from '@angular/platform-browser';
|
import {BrowserModule} from '@angular/platform-browser';
|
||||||
import {BrowserAnimationsModule} from '@angular/platform-browser/animations';
|
import {BrowserAnimationsModule} from '@angular/platform-browser/animations';
|
||||||
|
import {authInterceptorProviders} from '../helpers/auth.interceptor';
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
declarations: [
|
declarations: [
|
||||||
@@ -22,7 +23,7 @@ import {BrowserAnimationsModule} from '@angular/platform-browser/animations';
|
|||||||
|
|
||||||
],
|
],
|
||||||
exports: [RouterModule],
|
exports: [RouterModule],
|
||||||
providers: [{
|
providers: [authInterceptorProviders,provideHttpClient(withInterceptorsFromDi()),{
|
||||||
provide: HTTP_INTERCEPTORS,
|
provide: HTTP_INTERCEPTORS,
|
||||||
useClass: CustomHttpInterceptor,
|
useClass: CustomHttpInterceptor,
|
||||||
multi: true
|
multi: true
|
||||||
|
|||||||
@@ -19,6 +19,8 @@ export class AuthService {
|
|||||||
username,
|
username,
|
||||||
password
|
password
|
||||||
}, httpOptions);
|
}, httpOptions);
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
register(username: string, email: string, password: string): Observable<any> {
|
register(username: string, email: string, password: string): Observable<any> {
|
||||||
|
|||||||
@@ -2,8 +2,9 @@ import { Injectable } from '@angular/core';
|
|||||||
import { HttpClient } from '@angular/common/http';
|
import { HttpClient } from '@angular/common/http';
|
||||||
import { Observable } from 'rxjs';
|
import { Observable } from 'rxjs';
|
||||||
import { Tutorial } from '../models/tutorial.model';
|
import { Tutorial } from '../models/tutorial.model';
|
||||||
|
import {text} from 'node:stream/consumers';
|
||||||
|
|
||||||
const baseUrl = 'http://localhost:8080/api/tutorials';
|
const baseUrl = 'http://localhost:8080/api/public/tutorials';
|
||||||
|
|
||||||
@Injectable({
|
@Injectable({
|
||||||
providedIn: 'root'
|
providedIn: 'root'
|
||||||
@@ -12,29 +13,31 @@ export class TutorialService {
|
|||||||
|
|
||||||
constructor(private http: HttpClient) { }
|
constructor(private http: HttpClient) { }
|
||||||
|
|
||||||
getAll(): Observable<Tutorial[]> {
|
getAllPublic(): Observable<Tutorial[]> {
|
||||||
return this.http.get<Tutorial[]>(baseUrl);
|
return this.http.get<Tutorial[]>(baseUrl);
|
||||||
}
|
}
|
||||||
|
|
||||||
get(id: any): Observable<Tutorial> {
|
//
|
||||||
return this.http.get(`${baseUrl}/${id}`);
|
// get(id: any): Observable<Tutorial> {
|
||||||
}
|
// return this.http.get(`${baseUrl}/${id}`);
|
||||||
|
// }
|
||||||
create(data: any): Observable<any> {
|
//
|
||||||
return this.http.post(baseUrl, data);
|
// create(data: any): Observable<any> {
|
||||||
}
|
// return this.http.post(baseUrl, data);
|
||||||
|
// }
|
||||||
update(id: any, data: any): Observable<any> {
|
//
|
||||||
return this.http.put(`${baseUrl}/${id}`, data);
|
//
|
||||||
}
|
// update(id: any, data: any): Observable<any> {
|
||||||
|
// return this.http.put(`${baseUrl}/${id}`, data);
|
||||||
delete(id: any): Observable<any> {
|
// }
|
||||||
return this.http.delete(`${baseUrl}/${id}`);
|
//
|
||||||
}
|
// delete(id: any): Observable<any> {
|
||||||
|
// return this.http.delete(`${baseUrl}/${id}`);
|
||||||
deleteAll(): Observable<any> {
|
// }
|
||||||
return this.http.delete(baseUrl);
|
//
|
||||||
}
|
// deleteAll(): Observable<any> {
|
||||||
|
// return this.http.delete(baseUrl);
|
||||||
|
// }
|
||||||
|
|
||||||
findByTitle(title: any): Observable<Tutorial[]> {
|
findByTitle(title: any): Observable<Tutorial[]> {
|
||||||
return this.http.get<Tutorial[]>(`${baseUrl}?title=${title}`);
|
return this.http.get<Tutorial[]>(`${baseUrl}?title=${title}`);
|
||||||
|
|||||||
@@ -0,0 +1 @@
|
|||||||
|
<p>tutorial-add.component works!</p>
|
||||||
+23
@@ -0,0 +1,23 @@
|
|||||||
|
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
|
import { TutorialAddComponent } from './tutorial-add.component';
|
||||||
|
|
||||||
|
describe('TutorialAddComponent', () => {
|
||||||
|
let component: TutorialAddComponent;
|
||||||
|
let fixture: ComponentFixture<TutorialAddComponent>;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
await TestBed.configureTestingModule({
|
||||||
|
imports: [TutorialAddComponent]
|
||||||
|
})
|
||||||
|
.compileComponents();
|
||||||
|
|
||||||
|
fixture = TestBed.createComponent(TutorialAddComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
import { Component } from '@angular/core';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-tutorial-add.component',
|
||||||
|
imports: [],
|
||||||
|
templateUrl: './tutorial-add.component.html',
|
||||||
|
styleUrl: './tutorial-add.component.scss'
|
||||||
|
})
|
||||||
|
export class TutorialAddComponent {
|
||||||
|
|
||||||
|
}
|
||||||
+26
-3
@@ -1,6 +1,29 @@
|
|||||||
<p>tutorials-list.component works!</p>
|
<p>tutorials-list.component works!</p>
|
||||||
|
|
||||||
|
<button mat-raised-button routerLink="../tutorial-add">Add tutorial</button>
|
||||||
|
|
||||||
<mat-list role="list">
|
<mat-list role="list">
|
||||||
<mat-list-item role="listitem">Item 1</mat-list-item>
|
@for (tutorial of tutorials; track tutorial) {
|
||||||
<mat-list-item role="listitem">Item 2</mat-list-item>
|
<!--
|
||||||
<mat-list-item role="listitem">Item 3</mat-list-item>
|
<mat-list-item role="listitem">
|
||||||
|
{{tutorial.title}}
|
||||||
|
|
||||||
|
|
||||||
|
<span class="spacer"></span>
|
||||||
|
<button mat-button>
|
||||||
|
<mat-icon>edit</mat-icon>
|
||||||
|
</button>
|
||||||
|
<button mat-button>
|
||||||
|
<mat-icon>delete</mat-icon>
|
||||||
|
</button>
|
||||||
|
</mat-list-item>-->
|
||||||
|
<mat-option >
|
||||||
|
<div style="display:flex; justify-content: space-between">
|
||||||
|
<span>{{tutorial.title}}</span>
|
||||||
|
<span></span>
|
||||||
|
<span (click)="deleteOption($event, tutorial)">X</span>
|
||||||
|
</div>
|
||||||
|
</mat-option>
|
||||||
|
}
|
||||||
|
|
||||||
</mat-list>
|
</mat-list>
|
||||||
|
|||||||
+3
@@ -0,0 +1,3 @@
|
|||||||
|
.spacer{
|
||||||
|
flex: 1 1 auto;
|
||||||
|
}
|
||||||
|
|||||||
+78
-3
@@ -1,15 +1,90 @@
|
|||||||
import { Component } from '@angular/core';
|
import {Component, CUSTOM_ELEMENTS_SCHEMA, OnInit} from '@angular/core';
|
||||||
import {MatList, MatListItem} from '@angular/material/list';
|
import {MatList, MatListItem} from '@angular/material/list';
|
||||||
|
import {Tutorial} from '../../models/tutorial.model';
|
||||||
|
import {TutorialService} from '../../services/tutorial.service';
|
||||||
|
import {UserApiService} from '../user-api.service';
|
||||||
|
import {MatButton} from '@angular/material/button';
|
||||||
|
import {MatLine, MatOption} from '@angular/material/core';
|
||||||
|
import {MatIcon} from '@angular/material/icon';
|
||||||
|
import {TokenStorageService} from '../../services/token-storage.service';
|
||||||
|
import {EventBusService} from '../../_shared/event-bus.service';
|
||||||
|
import {EventData} from '../../_shared/event.class';
|
||||||
|
import {Router, RouterLink} from '@angular/router';
|
||||||
|
import {AuthService} from '../../services/auth.service';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-tutorials-list.component',
|
selector: 'app-tutorials-list.component',
|
||||||
imports: [
|
imports: [
|
||||||
MatList,
|
MatList,
|
||||||
MatListItem
|
MatListItem,
|
||||||
|
MatLine,
|
||||||
|
MatButton,
|
||||||
|
MatIcon,
|
||||||
|
MatOption,
|
||||||
|
RouterLink
|
||||||
],
|
],
|
||||||
|
schemas:[CUSTOM_ELEMENTS_SCHEMA],
|
||||||
templateUrl: './tutorials-list.component.html',
|
templateUrl: './tutorials-list.component.html',
|
||||||
styleUrl: './tutorials-list.component.scss'
|
styleUrl: './tutorials-list.component.scss'
|
||||||
})
|
})
|
||||||
export class TutorialsListComponent {
|
export class TutorialsListComponent implements OnInit {
|
||||||
|
tutorials?: Tutorial[];
|
||||||
|
|
||||||
|
constructor(private userApiService: UserApiService,
|
||||||
|
private storageService: TokenStorageService,
|
||||||
|
private eventBusService: EventBusService,
|
||||||
|
private router: Router, private authService: AuthService) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ngOnInit(): void {
|
||||||
|
this.eventBusService.on('logout', () => {
|
||||||
|
this.logout();
|
||||||
|
})
|
||||||
|
this.retrieveTutorials();
|
||||||
|
}
|
||||||
|
|
||||||
|
retrieveTutorials(): void {
|
||||||
|
this.userApiService.getUserAllTutorials()
|
||||||
|
.subscribe(
|
||||||
|
data => {
|
||||||
|
this.tutorials = data;
|
||||||
|
console.log(data);
|
||||||
|
},
|
||||||
|
error => {
|
||||||
|
console.log(error);
|
||||||
|
if (
|
||||||
|
(
|
||||||
|
error.status === 401
|
||||||
|
|| error.status === 500
|
||||||
|
|| error.status === 0
|
||||||
|
)
|
||||||
|
&& this.storageService.isLoggedIn()
|
||||||
|
) {
|
||||||
|
this.eventBusService.emit(new EventData('logout', null));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
deleteOption($event: MouseEvent, option: any) {
|
||||||
|
|
||||||
|
}
|
||||||
|
logout(): void {
|
||||||
|
this.authService.logout().subscribe({
|
||||||
|
next: res => {
|
||||||
|
console.log(res);
|
||||||
|
this.storageService.clean();
|
||||||
|
|
||||||
|
//window.location.reload();
|
||||||
|
this.router.navigate(['main/generate-image']).then(() => {
|
||||||
|
window.location.reload();
|
||||||
|
})
|
||||||
|
|
||||||
|
},
|
||||||
|
error: err => {
|
||||||
|
console.log(err);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -0,0 +1,16 @@
|
|||||||
|
import { TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
|
import { UserApiService } from './user-api.service';
|
||||||
|
|
||||||
|
describe('UserApiService', () => {
|
||||||
|
let service: UserApiService;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
TestBed.configureTestingModule({});
|
||||||
|
service = TestBed.inject(UserApiService);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should be created', () => {
|
||||||
|
expect(service).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
import { Injectable } from '@angular/core';
|
||||||
|
import {Observable} from 'rxjs';
|
||||||
|
import {Tutorial} from '../models/tutorial.model';
|
||||||
|
import {HttpClient} from '@angular/common/http';
|
||||||
|
|
||||||
|
@Injectable({
|
||||||
|
providedIn: 'root'
|
||||||
|
})
|
||||||
|
export class UserApiService {
|
||||||
|
baseUrl = 'http://localhost:8080/api/user';
|
||||||
|
|
||||||
|
constructor(private http: HttpClient) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
getUserAllTutorials(): Observable<Tutorial[]> {
|
||||||
|
return this.http.get<Tutorial[]>(`${this.baseUrl}/tutorials`,{withCredentials: true});
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -41,7 +41,9 @@ export class UserComponent implements OnInit {
|
|||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
this.observer.observe(['(max-width: 800px)']).subscribe((screenSize) => {
|
this.observer.observe(['(max-width: 800px)']).subscribe((screenSize) => {
|
||||||
this.isMobile = screenSize.matches;
|
this.isMobile = screenSize.matches;
|
||||||
});
|
})
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
toggleMenu() {
|
toggleMenu() {
|
||||||
if(this.isMobile){
|
if(this.isMobile){
|
||||||
|
|||||||
@@ -16,6 +16,10 @@ const USER_ROUTES: Routes = [
|
|||||||
path: 'tutorials-list',
|
path: 'tutorials-list',
|
||||||
loadComponent: () => import('../user-module/tutorials-list.component/tutorials-list.component').then((c) => c.TutorialsListComponent)
|
loadComponent: () => import('../user-module/tutorials-list.component/tutorials-list.component').then((c) => c.TutorialsListComponent)
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: 'tutorial-add',
|
||||||
|
loadComponent: () => import('../user-module/tutorial-add.component/tutorial-add.component').then((c) => c.TutorialAddComponent)
|
||||||
|
},
|
||||||
{
|
{
|
||||||
path: 'ai-models',
|
path: 'ai-models',
|
||||||
loadComponent: () => import('../user-module/ai-models.component/ai-models.component').then((c) => c.AiModelsComponent)
|
loadComponent: () => import('../user-module/ai-models.component/ai-models.component').then((c) => c.AiModelsComponent)
|
||||||
|
|||||||
@@ -0,0 +1,3 @@
|
|||||||
|
export const environment = {
|
||||||
|
default_page: 'main/generate-image'
|
||||||
|
};
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
export const environment = {
|
||||||
|
default_page: 'main/generate-image'
|
||||||
|
};
|
||||||
@@ -62,6 +62,9 @@ if (isMainModule(import.meta.url)) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Request handler used by the Angular CLI (for dev-server and during build) or Firebase Cloud Functions.
|
* Request handler used by the Angular CLI (for dev-server and during build) or Firebase Cloud Functions.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -12,7 +12,11 @@ import org.springframework.web.bind.annotation.*;
|
|||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
@CrossOrigin(origins = "http://localhost:4200, https://pony-sincere-chimp.ngrok-free.app/", maxAge = 3600, allowCredentials="true")
|
@CrossOrigin(origins = "http://localhost:4200, https://pony-sincere-chimp.ngrok-free.app/",
|
||||||
|
maxAge = 3600,
|
||||||
|
allowCredentials="true",
|
||||||
|
allowedHeaders = {"Content-Type", "Authorization", "X-Requested-With"}
|
||||||
|
)
|
||||||
@RestController
|
@RestController
|
||||||
@RequestMapping("/api/zhipuai")
|
@RequestMapping("/api/zhipuai")
|
||||||
public class ImageController {
|
public class ImageController {
|
||||||
|
|||||||
@@ -13,6 +13,8 @@ import com.jambotronGroup.jambotron.repository.UserRepository;
|
|||||||
import com.jambotronGroup.jambotron.security.jwt.JwtUtils;
|
import com.jambotronGroup.jambotron.security.jwt.JwtUtils;
|
||||||
import com.jambotronGroup.jambotron.security.services.UserDetailsImpl;
|
import com.jambotronGroup.jambotron.security.services.UserDetailsImpl;
|
||||||
import jakarta.validation.Valid;
|
import jakarta.validation.Valid;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.http.HttpHeaders;
|
import org.springframework.http.HttpHeaders;
|
||||||
import org.springframework.http.ResponseCookie;
|
import org.springframework.http.ResponseCookie;
|
||||||
@@ -34,6 +36,8 @@ import java.util.stream.Collectors;
|
|||||||
@RestController
|
@RestController
|
||||||
@RequestMapping("/api/auth")
|
@RequestMapping("/api/auth")
|
||||||
public class AuthController {
|
public class AuthController {
|
||||||
|
|
||||||
|
private static final Logger logger = LoggerFactory.getLogger(AuthController.class);
|
||||||
@Autowired
|
@Autowired
|
||||||
AuthenticationManager authenticationManager;
|
AuthenticationManager authenticationManager;
|
||||||
|
|
||||||
@@ -65,12 +69,16 @@ public class AuthController {
|
|||||||
.map(item -> item.getAuthority())
|
.map(item -> item.getAuthority())
|
||||||
.collect(Collectors.toList());
|
.collect(Collectors.toList());
|
||||||
|
|
||||||
|
logger.info("User {} authenticated successfully with roles: {}", userDetails.getUsername(), roles);
|
||||||
|
|
||||||
return ResponseEntity.ok().header(HttpHeaders.SET_COOKIE, jwtCookie.toString())
|
return ResponseEntity.ok().header(HttpHeaders.SET_COOKIE, jwtCookie.toString())
|
||||||
.body(new UserInfoResponse(
|
.body(new UserInfoResponse(
|
||||||
userDetails.getId(),
|
userDetails.getId(),
|
||||||
userDetails.getUsername(),
|
userDetails.getUsername(),
|
||||||
userDetails.getEmail(),
|
userDetails.getEmail(),
|
||||||
roles));
|
roles,
|
||||||
|
jwtCookie.toString()
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
@PostMapping("/signup")
|
@PostMapping("/signup")
|
||||||
|
|||||||
@@ -2,10 +2,20 @@ package com.jambotronGroup.jambotron.controllers;
|
|||||||
|
|
||||||
|
|
||||||
import com.jambotronGroup.jambotron.model.Tutorial;
|
import com.jambotronGroup.jambotron.model.Tutorial;
|
||||||
|
import com.jambotronGroup.jambotron.model.User;
|
||||||
import com.jambotronGroup.jambotron.repository.TutorialRepository;
|
import com.jambotronGroup.jambotron.repository.TutorialRepository;
|
||||||
|
import com.jambotronGroup.jambotron.repository.UserRepository;
|
||||||
|
import com.jambotronGroup.jambotron.security.AuthenticationFacade;
|
||||||
|
import com.jambotronGroup.jambotron.security.services.UserDetailsImpl;
|
||||||
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.http.HttpStatus;
|
import org.springframework.http.HttpStatus;
|
||||||
import org.springframework.http.ResponseEntity;
|
import org.springframework.http.ResponseEntity;
|
||||||
|
import org.springframework.security.authentication.AuthenticationManager;
|
||||||
|
import org.springframework.security.core.Authentication;
|
||||||
|
import org.springframework.security.core.annotation.AuthenticationPrincipal;
|
||||||
|
import org.springframework.security.core.context.SecurityContextHolder;
|
||||||
|
import org.springframework.security.core.userdetails.UserDetails;
|
||||||
import org.springframework.web.bind.annotation.*;
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
@@ -14,23 +24,77 @@ import java.util.Optional;
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
@CrossOrigin(origins = "${app.origin}", maxAge = 3600, allowCredentials="true")
|
@CrossOrigin(origins = "http://localhost:4200", maxAge = 3600, allowCredentials="true")
|
||||||
@RestController
|
@RestController
|
||||||
@RequestMapping("/api")
|
@RequestMapping("/api")
|
||||||
public class TutorialController {
|
public class TutorialController {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
AuthenticationFacade authenticationFacade;
|
||||||
|
@Autowired
|
||||||
|
UserRepository userRepository;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
TutorialRepository tutorialRepository;
|
TutorialRepository tutorialRepository;
|
||||||
|
|
||||||
@GetMapping("/tutorials")
|
@GetMapping("/user/tutorials")
|
||||||
|
public ResponseEntity<List<Tutorial>> getUserTutorials(@RequestParam(required = false) String title) {
|
||||||
|
try {
|
||||||
|
List<Tutorial> tutorials = new ArrayList<Tutorial>();
|
||||||
|
|
||||||
|
User user = userRepository.findById(authenticationFacade.getUserDetails().getId()).get();
|
||||||
|
|
||||||
|
|
||||||
|
if(title == null){
|
||||||
|
// If no title is provided, return all tutorials for the user
|
||||||
|
tutorialRepository.findByUserId(user.getId()).forEach(tutorials::add);
|
||||||
|
} else {
|
||||||
|
// If a title is provided, filter tutorials by user and title
|
||||||
|
tutorialRepository.findByUserIdAndTitle(user.getId(), title).forEach(tutorials::add);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tutorials.isEmpty()) {
|
||||||
|
return new ResponseEntity<>(HttpStatus.NO_CONTENT);
|
||||||
|
}
|
||||||
|
return new ResponseEntity<>(tutorials, HttpStatus.OK);
|
||||||
|
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
return new ResponseEntity<>(null, HttpStatus.INTERNAL_SERVER_ERROR);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@GetMapping("/public/tutorials")
|
||||||
public ResponseEntity<List<Tutorial>> getAllTutorials(@RequestParam(required = false) String title) {
|
public ResponseEntity<List<Tutorial>> getAllTutorials(@RequestParam(required = false) String title) {
|
||||||
try {
|
try {
|
||||||
List<Tutorial> tutorials = new ArrayList<Tutorial>();
|
List<Tutorial> tutorials = new ArrayList<Tutorial>();
|
||||||
|
|
||||||
|
/* Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
|
||||||
|
UserDetails userDetails = (UserDetails) authentication.getPrincipal();
|
||||||
|
String name = authenticationFacade.getAuthentication().getName();
|
||||||
|
|
||||||
|
User user = userRepository.findById(((UserDetailsImpl)userDetails).getId()).get();
|
||||||
|
if (user.getRoles().stream().anyMatch(role -> role.getName().name().equals("ROLE_ADMIN"))) {*/
|
||||||
|
/*
|
||||||
if (title == null)
|
if (title == null)
|
||||||
tutorialRepository.findAll().forEach(tutorials::add);
|
tutorialRepository.findAll().forEach(tutorials::add);
|
||||||
else
|
else
|
||||||
tutorialRepository.findByTitleContaining(title).forEach(tutorials::add);
|
tutorialRepository.findByTitleContaining(title).forEach(tutorials::add);
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* } else {
|
||||||
|
|
||||||
|
// If the user is not an admin, filter tutorials by user
|
||||||
|
tutorialRepository.findByUserId(user.getId()).forEach(tutorials::add);
|
||||||
|
if (tutorials.isEmpty()) {
|
||||||
|
return new ResponseEntity<>(HttpStatus.NO_CONTENT);
|
||||||
|
}
|
||||||
|
return new ResponseEntity<>(tutorials, HttpStatus.OK);
|
||||||
|
}*/
|
||||||
|
|
||||||
|
//all published tutorials without authentication
|
||||||
|
tutorialRepository.findByPublished(true).forEach(tutorials::add);
|
||||||
|
|
||||||
if (tutorials.isEmpty()) {
|
if (tutorials.isEmpty()) {
|
||||||
return new ResponseEntity<>(HttpStatus.NO_CONTENT);
|
return new ResponseEntity<>(HttpStatus.NO_CONTENT);
|
||||||
@@ -55,9 +119,15 @@ public class TutorialController {
|
|||||||
|
|
||||||
@PostMapping("/tutorials")
|
@PostMapping("/tutorials")
|
||||||
public ResponseEntity<Tutorial> createTutorial(@RequestBody Tutorial tutorial) {
|
public ResponseEntity<Tutorial> createTutorial(@RequestBody Tutorial tutorial) {
|
||||||
|
|
||||||
|
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
|
||||||
|
UserDetails userDetails = (UserDetails) authentication.getPrincipal();
|
||||||
|
|
||||||
|
User user = userRepository.findById(((UserDetailsImpl)userDetails).getId()).get();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
Tutorial _tutorial = tutorialRepository
|
Tutorial _tutorial = tutorialRepository
|
||||||
.save(new Tutorial(tutorial.getTitle(), tutorial.getDescription(), false));
|
.save(new Tutorial(tutorial.getTitle(), tutorial.getDescription(), false, user));
|
||||||
return new ResponseEntity<>(_tutorial, HttpStatus.CREATED);
|
return new ResponseEntity<>(_tutorial, HttpStatus.CREATED);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
return new ResponseEntity<>(null, HttpStatus.INTERNAL_SERVER_ERROR);
|
return new ResponseEntity<>(null, HttpStatus.INTERNAL_SERVER_ERROR);
|
||||||
|
|||||||
@@ -21,14 +21,19 @@ public class Tutorial {
|
|||||||
@Column(name = "published")
|
@Column(name = "published")
|
||||||
private boolean published;
|
private boolean published;
|
||||||
|
|
||||||
|
@ManyToOne(fetch = FetchType.LAZY, optional = false)
|
||||||
|
@JoinColumn(name = "userID", nullable = false)
|
||||||
|
private User user;
|
||||||
|
|
||||||
public Tutorial() {
|
public Tutorial() {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public Tutorial(String title, String description, boolean published) {
|
public Tutorial(String title, String description, boolean published, User user) {
|
||||||
this.title = title;
|
this.title = title;
|
||||||
this.description = description;
|
this.description = description;
|
||||||
this.published = published;
|
this.published = published;
|
||||||
|
this.user = user;
|
||||||
}
|
}
|
||||||
|
|
||||||
public long getId() {
|
public long getId() {
|
||||||
|
|||||||
@@ -10,11 +10,14 @@ public class UserInfoResponse {
|
|||||||
private String email;
|
private String email;
|
||||||
private List<String> roles;
|
private List<String> roles;
|
||||||
|
|
||||||
public UserInfoResponse(Long id, String username, String email, List<String> roles) {
|
private String token;
|
||||||
|
|
||||||
|
public UserInfoResponse(Long id, String username, String email, List<String> roles, String token) {
|
||||||
this.id = id;
|
this.id = id;
|
||||||
this.username = username;
|
this.username = username;
|
||||||
this.email = email;
|
this.email = email;
|
||||||
this.roles = roles;
|
this.roles = roles;
|
||||||
|
this.token = token;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Long getId() {
|
public Long getId() {
|
||||||
@@ -44,4 +47,12 @@ public class UserInfoResponse {
|
|||||||
public List<String> getRoles() {
|
public List<String> getRoles() {
|
||||||
return roles;
|
return roles;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getToken() {
|
||||||
|
return token;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setToken(String token) {
|
||||||
|
this.token = token;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,6 +10,9 @@ import java.util.List;
|
|||||||
|
|
||||||
@Repository
|
@Repository
|
||||||
public interface TutorialRepository extends JpaRepository<Tutorial, Long> {
|
public interface TutorialRepository extends JpaRepository<Tutorial, Long> {
|
||||||
|
|
||||||
|
List<Tutorial> findByUserId(Long userId);
|
||||||
|
List<Tutorial> findByUserIdAndTitle(Long userId, String title);
|
||||||
List<Tutorial> findByPublished(boolean published);
|
List<Tutorial> findByPublished(boolean published);
|
||||||
List<Tutorial> findByTitleContaining(String title);
|
List<Tutorial> findByTitleContaining(String title);
|
||||||
}
|
}
|
||||||
@@ -9,6 +9,8 @@ import java.util.Optional;
|
|||||||
|
|
||||||
@Repository
|
@Repository
|
||||||
public interface UserRepository extends JpaRepository<User, Long> {
|
public interface UserRepository extends JpaRepository<User, Long> {
|
||||||
|
|
||||||
|
|
||||||
Optional<User> findByUsername(String username);
|
Optional<User> findByUsername(String username);
|
||||||
|
|
||||||
Boolean existsByUsername(String username);
|
Boolean existsByUsername(String username);
|
||||||
|
|||||||
@@ -0,0 +1,29 @@
|
|||||||
|
package com.jambotronGroup.jambotron.security;
|
||||||
|
|
||||||
|
import com.jambotronGroup.jambotron.security.services.UserDetailsImpl;
|
||||||
|
import org.springframework.security.core.Authentication;
|
||||||
|
import org.springframework.security.core.context.SecurityContextHolder;
|
||||||
|
import org.springframework.security.core.userdetails.UserDetails;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
@Component
|
||||||
|
public class AuthenticationFacade implements IAuthenticationFacade {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Authentication getAuthentication() {
|
||||||
|
|
||||||
|
return SecurityContextHolder.getContext().getAuthentication();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public UserDetailsImpl getUserDetails() {
|
||||||
|
|
||||||
|
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
|
||||||
|
UserDetails userDetails = (UserDetails) authentication.getPrincipal();
|
||||||
|
|
||||||
|
return (UserDetailsImpl) userDetails;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
package com.jambotronGroup.jambotron.security;
|
||||||
|
|
||||||
|
import com.jambotronGroup.jambotron.security.services.UserDetailsImpl;
|
||||||
|
import org.springframework.security.core.Authentication;
|
||||||
|
|
||||||
|
public interface IAuthenticationFacade {
|
||||||
|
Authentication getAuthentication();
|
||||||
|
|
||||||
|
UserDetailsImpl getUserDetails();
|
||||||
|
}
|
||||||
@@ -43,10 +43,7 @@ public class WebSecurityConfig implements WebMvcConfigurer {// extends WebSecuri
|
|||||||
// authenticationManagerBuilder.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
|
// authenticationManagerBuilder.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
|
||||||
// }
|
// }
|
||||||
|
|
||||||
@Override
|
|
||||||
public void addCorsMappings(CorsRegistry registry) {
|
|
||||||
// Do not add any mappings to enable complete disabling of CORS.
|
|
||||||
}
|
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
public DaoAuthenticationProvider authenticationProvider() {
|
public DaoAuthenticationProvider authenticationProvider() {
|
||||||
@@ -106,7 +103,7 @@ public class WebSecurityConfig implements WebMvcConfigurer {// extends WebSecuri
|
|||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
|
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
|
||||||
http.csrf(csrf -> csrf.disable())
|
http.csrf(csrf -> csrf.disable()).cors(cors -> cors.disable())
|
||||||
.exceptionHandling(exception -> exception.authenticationEntryPoint(unauthorizedHandler))
|
.exceptionHandling(exception -> exception.authenticationEntryPoint(unauthorizedHandler))
|
||||||
.sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
|
.sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
|
||||||
.authorizeHttpRequests(auth ->
|
.authorizeHttpRequests(auth ->
|
||||||
@@ -118,6 +115,7 @@ public class WebSecurityConfig implements WebMvcConfigurer {// extends WebSecuri
|
|||||||
|
|
||||||
.requestMatchers("/main/**").permitAll()
|
.requestMatchers("/main/**").permitAll()
|
||||||
.requestMatchers("/api/test/**").permitAll()
|
.requestMatchers("/api/test/**").permitAll()
|
||||||
|
|
||||||
.requestMatchers("/api/tutorials").permitAll()
|
.requestMatchers("/api/tutorials").permitAll()
|
||||||
|
|
||||||
.requestMatchers("/api/zhipuai/image/**").permitAll()
|
.requestMatchers("/api/zhipuai/image/**").permitAll()
|
||||||
@@ -132,6 +130,12 @@ public class WebSecurityConfig implements WebMvcConfigurer {// extends WebSecuri
|
|||||||
|
|
||||||
.requestMatchers("/api/settings").permitAll()
|
.requestMatchers("/api/settings").permitAll()
|
||||||
.requestMatchers("/api/system/**").permitAll()
|
.requestMatchers("/api/system/**").permitAll()
|
||||||
|
|
||||||
|
// Allow public access to tutorials (without login)
|
||||||
|
.requestMatchers("/api/public/tutorials").permitAll()
|
||||||
|
|
||||||
|
.requestMatchers("/api/user").permitAll()
|
||||||
|
|
||||||
.anyRequest().authenticated()
|
.anyRequest().authenticated()
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ public class AuthEntryPointJwt implements AuthenticationEntryPoint {
|
|||||||
throws IOException, ServletException {
|
throws IOException, ServletException {
|
||||||
logger.error("Unauthorized error: {}", authException.getMessage());
|
logger.error("Unauthorized error: {}", authException.getMessage());
|
||||||
|
|
||||||
|
response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Error: Unauthorized");
|
||||||
response.setContentType(MediaType.APPLICATION_JSON_VALUE);
|
response.setContentType(MediaType.APPLICATION_JSON_VALUE);
|
||||||
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
|
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
|
||||||
|
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ spring.jpa.show-sql=true
|
|||||||
|
|
||||||
# App Properties
|
# App Properties
|
||||||
app.jwtSecret= ======================spring=back====================
|
app.jwtSecret= ======================spring=back====================
|
||||||
app.jwtExpirationMs= 30000
|
app.jwtExpirationMs= 800000
|
||||||
app.jwtCookieName=springangularts
|
app.jwtCookieName=springangularts
|
||||||
app.origin=http://localhost:4200
|
app.origin=http://localhost:4200
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user