l'authent fonctionne vite fait

This commit is contained in:
Guams 2024-12-21 13:46:22 +01:00
parent aeb3579d8b
commit 0cd1121d70
22 changed files with 289 additions and 39 deletions

33
package-lock.json generated
View File

@ -20,6 +20,7 @@
"@angular/ssr": "^18.2.12", "@angular/ssr": "^18.2.12",
"express": "^4.18.2", "express": "^4.18.2",
"luxon": "^3.5.0", "luxon": "^3.5.0",
"ngx-cookie-service": "^18.0.0",
"primeicons": "^7.0.0", "primeicons": "^7.0.0",
"primeng": "^17.18.10", "primeng": "^17.18.10",
"rxjs": "~7.8.0", "rxjs": "~7.8.0",
@ -32,6 +33,7 @@
"@angular/compiler-cli": "^18.2.0", "@angular/compiler-cli": "^18.2.0",
"@types/express": "^4.17.17", "@types/express": "^4.17.17",
"@types/jasmine": "~5.1.0", "@types/jasmine": "~5.1.0",
"@types/jwt-decode": "^3.1.0",
"@types/node": "^18.18.0", "@types/node": "^18.18.0",
"jasmine-core": "~5.2.0", "jasmine-core": "~5.2.0",
"karma": "~6.4.0", "karma": "~6.4.0",
@ -4462,6 +4464,16 @@
"dev": true, "dev": true,
"license": "MIT" "license": "MIT"
}, },
"node_modules/@types/jwt-decode": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/@types/jwt-decode/-/jwt-decode-3.1.0.tgz",
"integrity": "sha512-tthwik7TKkou3mVnBnvVuHnHElbjtdbM63pdBCbZTirCt3WAdM73Y79mOri7+ljsS99ZVwUFZHLMxJuJnv/z1w==",
"deprecated": "This is a stub types definition. jwt-decode provides its own type definitions, so you do not need this installed.",
"dev": true,
"dependencies": {
"jwt-decode": "*"
}
},
"node_modules/@types/mime": { "node_modules/@types/mime": {
"version": "1.3.5", "version": "1.3.5",
"resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.5.tgz", "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.5.tgz",
@ -8479,6 +8491,15 @@
], ],
"license": "MIT" "license": "MIT"
}, },
"node_modules/jwt-decode": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/jwt-decode/-/jwt-decode-4.0.0.tgz",
"integrity": "sha512-+KJGIyHgkGuIq3IEBNftfhW/LfWhXUIY6OmyVWjliu5KH1y0fw7VQ8YndE2O4qZdMSd9SqbnC8GOcZEy0Om7sA==",
"dev": true,
"engines": {
"node": ">=18"
}
},
"node_modules/karma": { "node_modules/karma": {
"version": "6.4.4", "version": "6.4.4",
"resolved": "https://registry.npmjs.org/karma/-/karma-6.4.4.tgz", "resolved": "https://registry.npmjs.org/karma/-/karma-6.4.4.tgz",
@ -9798,6 +9819,18 @@
"dev": true, "dev": true,
"license": "MIT" "license": "MIT"
}, },
"node_modules/ngx-cookie-service": {
"version": "18.0.0",
"resolved": "https://registry.npmjs.org/ngx-cookie-service/-/ngx-cookie-service-18.0.0.tgz",
"integrity": "sha512-hkkUckzZTXXWtFgvVkT2hg6mwYMLXioXDZWBsVCOy9gYkADjsj0N5VViO7eo2izQ0VcMPd/Etog1trf/T4oZMQ==",
"dependencies": {
"tslib": "^2.6.2"
},
"peerDependencies": {
"@angular/common": "^18.0.0-rc.0",
"@angular/core": "^18.0.0-rc.0"
}
},
"node_modules/nice-napi": { "node_modules/nice-napi": {
"version": "1.0.2", "version": "1.0.2",
"resolved": "https://registry.npmjs.org/nice-napi/-/nice-napi-1.0.2.tgz", "resolved": "https://registry.npmjs.org/nice-napi/-/nice-napi-1.0.2.tgz",

View File

@ -23,6 +23,7 @@
"@angular/ssr": "^18.2.12", "@angular/ssr": "^18.2.12",
"express": "^4.18.2", "express": "^4.18.2",
"luxon": "^3.5.0", "luxon": "^3.5.0",
"ngx-cookie-service": "^18.0.0",
"primeicons": "^7.0.0", "primeicons": "^7.0.0",
"primeng": "^17.18.10", "primeng": "^17.18.10",
"rxjs": "~7.8.0", "rxjs": "~7.8.0",
@ -35,6 +36,7 @@
"@angular/compiler-cli": "^18.2.0", "@angular/compiler-cli": "^18.2.0",
"@types/express": "^4.17.17", "@types/express": "^4.17.17",
"@types/jasmine": "~5.1.0", "@types/jasmine": "~5.1.0",
"@types/jwt-decode": "^3.1.0",
"@types/node": "^18.18.0", "@types/node": "^18.18.0",
"jasmine-core": "~5.2.0", "jasmine-core": "~5.2.0",
"karma": "~6.4.0", "karma": "~6.4.0",

View File

@ -1,5 +0,0 @@
img {
width: 100%;
height: 25rem;
object-fit: cover;
}

View File

@ -1,3 +1 @@
<img src="assets/banner.png" height="494" width="1494"/>
<p-menubar [model]="items" />
<router-outlet></router-outlet> <router-outlet></router-outlet>

View File

@ -1,7 +1,8 @@
import {Component, CUSTOM_ELEMENTS_SCHEMA} from '@angular/core'; import {Component} from '@angular/core';
import {MenubarModule} from 'primeng/menubar'; import {MenubarModule} from 'primeng/menubar';
import {MenuItem} from 'primeng/api'; import {MenuItem} from 'primeng/api';
import {FloatLabelModule} from 'primeng/floatlabel'; import {FloatLabelModule} from 'primeng/floatlabel';
import {CookieService} from 'ngx-cookie-service';
@Component({ @Component({
selector: 'app-root', selector: 'app-root',
@ -11,20 +12,8 @@ import {FloatLabelModule} from 'primeng/floatlabel';
styleUrl: './app.component.css' styleUrl: './app.component.css'
}) })
export class AppComponent{ export class AppComponent{
items: MenuItem[] | undefined items: MenuItem[] = []
constructor() { constructor(private cookieService: CookieService) {
this.items = [
{
label: 'Accueil',
icon: 'pi pi-home',
routerLink: '/'
},
{
label: 'Se connecter',
icon: 'pi pi-sign-in',
routerLink : '/login'
}
]
} }
} }

View File

@ -1,14 +1,16 @@
import {ApplicationConfig, provideZoneChangeDetection} from '@angular/core'; import {ApplicationConfig, importProvidersFrom, provideZoneChangeDetection} from '@angular/core';
import {provideRouter} from '@angular/router'; import {provideRouter} from '@angular/router';
import {routes} from './app.routes'; import {routes} from './app.routes';
import {provideClientHydration} from '@angular/platform-browser'; import {provideClientHydration} from '@angular/platform-browser';
import {provideHttpClient, withFetch} from '@angular/common/http'; import {provideHttpClient, withFetch} from '@angular/common/http';
import {BrowserAnimationsModule} from '@angular/platform-browser/animations';
export const appConfig: ApplicationConfig = { export const appConfig: ApplicationConfig = {
providers: [ providers: [
provideZoneChangeDetection({eventCoalescing: true}), provideZoneChangeDetection({eventCoalescing: true}),
provideRouter(routes), provideRouter(routes),
provideClientHydration(), provideClientHydration(),
provideHttpClient(withFetch())] provideHttpClient(withFetch()),
importProvidersFrom([BrowserAnimationsModule])]
}; };

View File

@ -1,8 +1,12 @@
import {Routes} from '@angular/router'; import {Routes} from '@angular/router';
import {LoginComponent} from './login/login.component'; import {LoginComponent} from './login/login.component';
import {HomeComponent} from './home/home.component'; import {HomeComponent} from './home/home.component';
import {RegisterComponent} from './register/register.component';
import {LogoutComponent} from './logout/logout.component';
export const routes: Routes = [ export const routes: Routes = [
{path: '', component: HomeComponent}, {path: '', component: HomeComponent},
{path: 'login', component: LoginComponent} {path: 'login', component: LoginComponent},
{path: 'register', component: RegisterComponent},
{path: 'logout', component: LogoutComponent}
]; ];

View File

@ -0,0 +1,5 @@
img {
width: 100%;
height: 25rem;
object-fit: cover;
}

View File

@ -0,0 +1,2 @@
<img src="assets/banner.png" height="494" width="1494"/>
<p-menubar [model]="items" />

View File

@ -0,0 +1,58 @@
import { Component } from '@angular/core';
import {CookieService} from 'ngx-cookie-service';
import {MenuItem} from 'primeng/api';
import {MenubarModule} from 'primeng/menubar';
import {RouterOutlet} from '@angular/router';
@Component({
selector: 'app-header',
standalone: true,
imports: [
MenubarModule,
RouterOutlet
],
templateUrl: './header.component.html',
styleUrl: './header.component.css'
})
export class HeaderComponent {
items: MenuItem[] = [];
constructor(private cookieService: CookieService) {
if (this.cookieService.get('author')) {
this.items = [
{
label: 'Accueil',
icon: 'pi pi-home',
routerLink: '/'
},
{
label: 'Se déconnecter',
icon: 'pi pi-sign-out',
routerLink: '/logout'
},
{
label: 'Mon profil',
icon: 'pi pi-user',
routerLink: '/me'
}
];
} else {
this.items = [
{
label: 'Accueil',
icon: 'pi pi-home',
routerLink: '/'
},
{
label: 'Se connecter',
icon: 'pi pi-sign-in',
routerLink: '/login'
},
{
label: "S'inscrire",
icon: 'pi pi-user-plus',
routerLink: '/register'
}
];
}
}
}

View File

@ -1,3 +1,5 @@
<app-header></app-header>
<p-toast></p-toast>
<div class="main-content"> <div class="main-content">
<article> <article>
<img class="blog-img" src="data:image/jpeg;base64,image " alt="article image"> <img class="blog-img" src="data:image/jpeg;base64,image " alt="article image">
@ -16,4 +18,4 @@
</article> </article>
</div> </div>
{{ authors | json }} {{ actualAuthor | json }}

View File

@ -5,24 +5,45 @@ import {AuthorService} from '../services/author.service';
import {Author} from '../model/author'; import {Author} from '../model/author';
import {JsonPipe} from '@angular/common'; import {JsonPipe} from '@angular/common';
import {Subscription} from 'rxjs'; import {Subscription} from 'rxjs';
import {PostService} from '../services/post.service';
import {Header, MessageService} from 'primeng/api';
import {HeaderComponent} from '../header/header.component';
import {ToastModule} from 'primeng/toast';
import {CookieService} from 'ngx-cookie-service';
@Component({ @Component({
selector: 'app-home', selector: 'app-home',
standalone: true, standalone: true,
imports: [ imports: [
AvatarModule, AvatarModule,
ButtonDirective,
Button, Button,
HeaderComponent,
ToastModule,
JsonPipe JsonPipe
], ],
providers: [
MessageService,
],
templateUrl: './home.component.html', templateUrl: './home.component.html',
styleUrl: './home.component.css' styleUrl: './home.component.css'
}) })
export class HomeComponent implements OnDestroy { export class HomeComponent implements OnDestroy {
actualAuthor: Author | undefined;
authors: Author[] = [] authors: Author[] = []
subs: Subscription[] = [] subs: Subscription[] = []
constructor(private authorService: AuthorService) { constructor(
private authorService: AuthorService,
private postService: PostService,
private messageService: MessageService,
private cookieService: CookieService) {
if (this.cookieService.get('author')) {
this.actualAuthor = JSON.parse(this.cookieService.get('author'));
if (this.cookieService.get("just-authenticated")) {
this.successMessage('Connecté avec succès', `Heureux de vous revoir ${this.actualAuthor?.name}`);
}
}
this.subs.push(authorService.list() this.subs.push(authorService.list()
.subscribe({ .subscribe({
next: (authors: Author[]) => this.authors = authors, next: (authors: Author[]) => this.authors = authors,
@ -30,6 +51,16 @@ export class HomeComponent implements OnDestroy {
})); }));
} }
successMessage(summary: string, detail: string): void {
this.messageService.add({
severity: 'success',
summary: summary,
detail: detail,
life: 1500,
closable: false
});
}
ngOnDestroy(): void { ngOnDestroy(): void {
this.subs.forEach(sub => sub.unsubscribe()); this.subs.forEach(sub => sub.unsubscribe());
} }

View File

@ -1,5 +1,15 @@
<p-toast></p-toast>
<app-header></app-header>
<label for="username">Nom d'utilisateur</label> <label for="username">Nom d'utilisateur</label>
<input id="username" placeholder="Entrez votre nom d'utilisateur" pInputText [(ngModel)]="name"/> <input id="username" placeholder="Jean Zay" pInputText [(ngModel)]="name"/>
<label for="password">Mot de passe</label> <label for="password">Mot de passe</label>
<input id="password" placeholder="Entrez votre mot de passe" pInputText [(ngModel)]="name"/> <input id="password" placeholder="motDePasseTrèsSecret" pInputText [(ngModel)]="password"/>
<label for="confirm-password">Confirmez le mot de passe</label>
<input id="confirm-password" placeholder="motDePasseTrèsSecret" pInputText [(ngModel)]="confirmPassword" (keyup.enter)="sendLogins()" />
<p-button
label="Se connecter"
icon="pi pi-check"
(onClick)="sendLogins()"
/>
{{ actualAuthor | json }}

View File

@ -1,18 +1,84 @@
import {Component, CUSTOM_ELEMENTS_SCHEMA} from '@angular/core'; import {Component, CUSTOM_ELEMENTS_SCHEMA, OnDestroy} from '@angular/core';
import {FormsModule} from '@angular/forms'; import {FormsModule} from '@angular/forms';
import {InputTextModule} from 'primeng/inputtext'; import {InputTextModule} from 'primeng/inputtext';
import {Button} from 'primeng/button';
import {AuthorService} from '../services/author.service';
import {ToastModule} from 'primeng/toast';
import {MessageService} from 'primeng/api';
import {Author} from '../model/author';
import {Subscription, switchMap} from 'rxjs';
import {JsonPipe} from '@angular/common';
import { CookieService } from 'ngx-cookie-service';
import {HeaderComponent} from '../header/header.component';
import {Router} from '@angular/router';
@Component({ @Component({
selector: 'app-login', selector: 'app-login',
standalone: true, standalone: true,
imports: [ imports: [
FormsModule, FormsModule,
InputTextModule InputTextModule,
Button,
ToastModule,
JsonPipe,
HeaderComponent
], ],
templateUrl: './login.component.html', templateUrl: './login.component.html',
styleUrl: './login.component.css' styleUrl: './login.component.css',
providers: [
MessageService,
]
}) })
export class LoginComponent { export class LoginComponent implements OnDestroy {
name: string = ""; name: string = "";
actualAuthor: Author | undefined;
password: string = "";
confirmPassword: string = "";
subs: Subscription[] = [];
constructor(private authorService: AuthorService,
private messageService: MessageService,
private cookieService: CookieService,
private router: Router) {}
sendLogins(): void {
if (this.password === this.confirmPassword) {
this.subs.push
(
this.authorService.login(this.name, this.password).pipe(
switchMap((tokenObj: any) => this.authorService.me(tokenObj.token)))
.subscribe({
next: (author: Author) => {
this.cookieService.set("author", JSON.stringify(author));
this.cookieService.set("just-authenticated", "true");
this.getAuthorCookie();
this.router.navigate(['/']);
},
error: (err) => this.failureMessage('Erreur de connexion', err.message)
})
);
} else {
this.failureMessage('Erreur de connexion', 'Les deux mots de passe ne correspondent pas')
}
}
getAuthorCookie(): void {
this.actualAuthor = JSON.parse(this.cookieService.get("author"));
}
failureMessage(summary: string, detail: string): void {
this.messageService.add({
severity: 'error',
summary: summary,
detail: detail,
life: 1500,
closable: false
});
}
ngOnDestroy(): void {
this.subs.forEach(sub => sub.unsubscribe());
}
} }

View File

View File

@ -0,0 +1,2 @@
<app-header></app-header>
<h1>Déconnexion...</h1>

View File

@ -0,0 +1,24 @@
import {Component, OnInit} from '@angular/core';
import {CookieService} from 'ngx-cookie-service';
import {HeaderComponent} from '../header/header.component';
import {Router} from '@angular/router';
@Component({
selector: 'app-logout',
standalone: true,
imports: [
HeaderComponent
],
templateUrl: './logout.component.html',
styleUrl: './logout.component.css'
})
export class LogoutComponent implements OnInit{
constructor(private cookiesService: CookieService,
private router: Router) { }
ngOnInit(): void {
this.cookiesService.deleteAll();
this.router.navigate(['/']);
}
}

View File

@ -2,4 +2,5 @@ export interface Author {
id: string id: string
name: string name: string
avatar: string avatar: string
role: string
} }

View File

View File

@ -0,0 +1,3 @@
<app-header></app-header>
<p>register works!</p>

View File

@ -0,0 +1,15 @@
import { Component } from '@angular/core';
import {HeaderComponent} from '../header/header.component';
@Component({
selector: 'app-register',
standalone: true,
imports: [
HeaderComponent
],
templateUrl: './register.component.html',
styleUrl: './register.component.css'
})
export class RegisterComponent {
}

View File

@ -1,5 +1,5 @@
import {Injectable} from '@angular/core'; import {Injectable} from '@angular/core';
import {HttpClient} from '@angular/common/http'; import {HttpClient, HttpHeaders} from '@angular/common/http';
import {Observable} from 'rxjs'; import {Observable} from 'rxjs';
import {Author} from '../model/author'; import {Author} from '../model/author';
@ -8,11 +8,19 @@ import {Author} from '../model/author';
}) })
export class AuthorService { export class AuthorService {
constructor(private httpClient: HttpClient) { constructor(private httpClient: HttpClient) {}
}
list(): Observable<Author[]> { list(): Observable<Author[]> {
return this.httpClient.get<Author[]>("http://localhost:8080/api/authors"); return this.httpClient.get<Author[]>("http://localhost:8080/api/authors");
} }
login(name: string, password: string) {
return this.httpClient.post<any>("http://localhost:8080/api/authors/login", {name: name, password: password})
}
me(token: string): Observable<Author> {
const headers = new HttpHeaders().set("Authorization", "Bearer " + token);
console.log(headers)
return this.httpClient.get<Author>("http://localhost:8080/api/authors/me", {'headers': headers});
}
} }