Refactorization and add metrics system.
This commit is contained in:
BIN
keystore.p12
Executable file → Normal file
BIN
keystore.p12
Executable file → Normal file
Binary file not shown.
2
pom.xml
2
pom.xml
@@ -5,7 +5,7 @@
|
||||
|
||||
<groupId>org.codiki</groupId>
|
||||
<artifactId>codiki</artifactId>
|
||||
<version>1.0.1</version>
|
||||
<version>1.1.0</version>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<name>codiki</name>
|
||||
|
||||
@@ -46,8 +46,16 @@ public class AccountController {
|
||||
|
||||
@JsonView(View.UserDTO.class)
|
||||
@PostMapping("/login")
|
||||
public User login(@RequestBody final User pUser) throws BadCredentialsException {
|
||||
return accountService.authenticate(pUser);
|
||||
public User login(@RequestBody final User pUser, HttpServletResponse pResponse) throws BadCredentialsException {
|
||||
User result = null;
|
||||
|
||||
try {
|
||||
result = accountService.authenticate(pUser);
|
||||
} catch(BadCredentialsException ex) {
|
||||
pResponse.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@GetMapping("/logout")
|
||||
|
||||
14
src/main/java/org/codiki/core/config/CoreConfig.java
Normal file
14
src/main/java/org/codiki/core/config/CoreConfig.java
Normal file
@@ -0,0 +1,14 @@
|
||||
package org.codiki.core.config;
|
||||
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
import java.time.Clock;
|
||||
|
||||
@Configuration
|
||||
public class CoreConfig {
|
||||
@Bean
|
||||
public Clock clock() {
|
||||
return Clock.systemDefaultZone();
|
||||
}
|
||||
}
|
||||
37
src/main/java/org/codiki/core/entities/dto/Metrics.java
Normal file
37
src/main/java/org/codiki/core/entities/dto/Metrics.java
Normal file
@@ -0,0 +1,37 @@
|
||||
package org.codiki.core.entities.dto;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
public class Metrics {
|
||||
/** Rest API version number. */
|
||||
private String version;
|
||||
/** Application start date. */
|
||||
private LocalDateTime uptime;
|
||||
/** Platform name. */
|
||||
private String platform;
|
||||
|
||||
public String getVersion() {
|
||||
return version;
|
||||
}
|
||||
|
||||
public void setVersion(String version) {
|
||||
this.version = version;
|
||||
}
|
||||
|
||||
public LocalDateTime getUptime() {
|
||||
return uptime;
|
||||
}
|
||||
|
||||
public void setUptime(LocalDateTime uptime) {
|
||||
this.uptime = uptime;
|
||||
}
|
||||
|
||||
public String getPlatform() {
|
||||
return platform;
|
||||
}
|
||||
|
||||
public void setPlatform(String platform) {
|
||||
this.platform = platform;
|
||||
}
|
||||
}
|
||||
26
src/main/java/org/codiki/metrics/MetricsController.java
Normal file
26
src/main/java/org/codiki/metrics/MetricsController.java
Normal file
@@ -0,0 +1,26 @@
|
||||
package org.codiki.metrics;
|
||||
|
||||
import org.codiki.core.entities.dto.Metrics;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/api/metrics")
|
||||
public class MetricsController {
|
||||
|
||||
private MetricsService metricsService;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
* @param metricsService Metrics service.
|
||||
*/
|
||||
public MetricsController(MetricsService metricsService) {
|
||||
this.metricsService = metricsService;
|
||||
}
|
||||
|
||||
@GetMapping("/healthCheck")
|
||||
public Metrics healthCheck() {
|
||||
return metricsService.getMetrics();
|
||||
}
|
||||
}
|
||||
44
src/main/java/org/codiki/metrics/MetricsService.java
Normal file
44
src/main/java/org/codiki/metrics/MetricsService.java
Normal file
@@ -0,0 +1,44 @@
|
||||
package org.codiki.metrics;
|
||||
|
||||
import org.codiki.core.entities.dto.Metrics;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.time.Clock;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
@Service
|
||||
public class MetricsService {
|
||||
/** Application start date. */
|
||||
private LocalDateTime uptime;
|
||||
/** Application version number. */
|
||||
private String appVersion;
|
||||
/** Platform name. */
|
||||
private String appPlatform;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
* @param clock System clock.
|
||||
*/
|
||||
public MetricsService(Clock clock,
|
||||
@Value("${app.version}") String appVersion,
|
||||
@Value("${app.platform}") String appPlatform) {
|
||||
uptime = LocalDateTime.now(clock);
|
||||
this.appVersion = appVersion;
|
||||
this.appPlatform = appPlatform;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns application metrics like uptime, version number or platform name and others.
|
||||
* @return Application metrics.
|
||||
*/
|
||||
public Metrics getMetrics() {
|
||||
Metrics metrics = new Metrics();
|
||||
|
||||
metrics.setUptime(uptime);
|
||||
metrics.setVersion(appVersion);
|
||||
metrics.setPlatform(appPlatform);
|
||||
|
||||
return metrics;
|
||||
}
|
||||
}
|
||||
@@ -18,6 +18,7 @@ import org.codiki.core.entities.persistence.User;
|
||||
import org.codiki.core.repositories.PostRepository;
|
||||
import org.codiki.core.services.ParserService;
|
||||
import org.codiki.core.services.UserService;
|
||||
import org.codiki.core.utils.StringUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.data.domain.PageRequest;
|
||||
import org.springframework.web.bind.annotation.DeleteMapping;
|
||||
@@ -36,23 +37,37 @@ import com.fasterxml.jackson.annotation.JsonView;
|
||||
public class PostController {
|
||||
|
||||
private static final int LIMIT_POSTS_HOME = 20;
|
||||
|
||||
@Autowired
|
||||
/** Service to parse post content. */
|
||||
private ParserService parserService;
|
||||
|
||||
@Autowired
|
||||
/** Posts repository. */
|
||||
private PostRepository postRepository;
|
||||
|
||||
@Autowired
|
||||
/** Posts business service. */
|
||||
private PostService postService;
|
||||
|
||||
@Autowired
|
||||
/** Users business service. */
|
||||
private UserService userService;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
* @param parserService Service to parse post content.
|
||||
* @param postRepository Posts repository.
|
||||
* @param postService Posts business service.
|
||||
* @param userService Users business service.
|
||||
*/
|
||||
public PostController(ParserService parserService,
|
||||
PostRepository postRepository,
|
||||
PostService postService,
|
||||
UserService userService) {
|
||||
this.parserService = parserService;
|
||||
this.postRepository = postRepository;
|
||||
this.postService = postService;
|
||||
this.userService = userService;
|
||||
}
|
||||
|
||||
@GetMapping
|
||||
public List<PostDTO> getAll() {
|
||||
return StreamSupport.stream(postRepository.findAll().spliterator(), false)
|
||||
.map(PostDTO::new).collect(Collectors.toList());
|
||||
.map(PostDTO::new)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@JsonView(View.PostDTO.class)
|
||||
@@ -70,16 +85,11 @@ public class PostController {
|
||||
@GetMapping("/{postKey}/source")
|
||||
public Post getByKeyAndSource(@PathVariable("postKey")final String pPostKey,
|
||||
final HttpServletResponse response) {
|
||||
Post result = null;
|
||||
|
||||
final Optional<Post> post = postRepository.getByKey(pPostKey);
|
||||
if(post.isPresent()) {
|
||||
result = post.get();
|
||||
} else {
|
||||
return postRepository.getByKey(pPostKey)
|
||||
.orElseGet(() -> {
|
||||
response.setStatus(HttpServletResponse.SC_NOT_FOUND);
|
||||
}
|
||||
|
||||
return result;
|
||||
return null;
|
||||
});
|
||||
}
|
||||
|
||||
@JsonView(View.PostDTO.class)
|
||||
@@ -121,9 +131,10 @@ public class PostController {
|
||||
@JsonView(View.PostDTO.class)
|
||||
@PostMapping("/preview")
|
||||
public Post preview(@RequestBody final Post pPost) {
|
||||
pPost.setImage(pPost.getImage() == null || "".equals(pPost.getImage())
|
||||
pPost.setImage(StringUtils.isNull(pPost.getImage())
|
||||
? "https://news-cdn.softpedia.com/images/news2/this-is-the-default-wallpaper-of-the-gnome-3-20-desktop-environment-500743-2.jpg"
|
||||
: pPost.getImage());
|
||||
|
||||
pPost.setText(parserService.parse(pPost.getText()));
|
||||
|
||||
return pPost;
|
||||
@@ -133,15 +144,7 @@ public class PostController {
|
||||
@PostMapping("/")
|
||||
public Post insert(@RequestBody final PostDTO pPost, final HttpServletRequest pRequest,
|
||||
final HttpServletResponse pResponse, final Principal pPrincipal) {
|
||||
Post result = null;
|
||||
|
||||
Optional<Post> postCreated = postService.insert(pPost, pRequest, pResponse, pPrincipal);
|
||||
|
||||
if(postCreated.isPresent()) {
|
||||
result = postCreated.get();
|
||||
}
|
||||
|
||||
return result;
|
||||
return postService.insert(pPost, pRequest, pResponse, pPrincipal).orElse(null);
|
||||
}
|
||||
|
||||
@PutMapping("/")
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
app:
|
||||
name: Codiki
|
||||
description: A wiki application.
|
||||
version: 1.2.0
|
||||
platform: develop
|
||||
|
||||
codiki:
|
||||
files:
|
||||
|
||||
@@ -36,6 +36,7 @@ import { ForbiddenComponent } from './forbidden/forbidden.component';
|
||||
import { SearchComponent } from './search/search.component';
|
||||
import { SigninComponent } from './signin/signin.component';
|
||||
import { VersionRevisionComponent } from './version-revisions/version-revisions.component';
|
||||
import { HealthCheckComponent } from './health-check/health-check.component';
|
||||
|
||||
// Reusable components
|
||||
import { PostCardComponent } from './core/post-card/post-card.component';
|
||||
@@ -57,6 +58,7 @@ import { CreateUpdatePostService } from './posts/create-update/create-update-pos
|
||||
import { SearchService } from './search/search.service';
|
||||
import { SigninService } from './signin/signin.service';
|
||||
import { VersionRevisionService } from './version-revisions/version-revisions.service';
|
||||
import { HealthCheckService } from './health-check/health-check.service';
|
||||
|
||||
@NgModule({
|
||||
declarations: [
|
||||
@@ -81,7 +83,8 @@ import { VersionRevisionService } from './version-revisions/version-revisions.se
|
||||
SearchComponent,
|
||||
SearchBarComponent,
|
||||
ProgressBarComponent,
|
||||
ForbiddenComponent
|
||||
ForbiddenComponent,
|
||||
HealthCheckComponent
|
||||
],
|
||||
imports: [
|
||||
BrowserModule,
|
||||
@@ -110,6 +113,7 @@ import { VersionRevisionService } from './version-revisions/version-revisions.se
|
||||
CreateUpdatePostService,
|
||||
VersionRevisionService,
|
||||
SearchService,
|
||||
HealthCheckService,
|
||||
{ provide: HTTP_INTERCEPTORS, useClass: UnauthorizedInterceptor, multi: true }
|
||||
],
|
||||
bootstrap: [AppComponent]
|
||||
|
||||
@@ -14,6 +14,7 @@ import { PostComponent } from './posts/post.component';
|
||||
import { ByCategoryComponent } from './posts/byCategory/by-category.component';
|
||||
import { CreateUpdatePostComponent } from './posts/create-update/create-update-post.component';
|
||||
import { VersionRevisionComponent } from './version-revisions/version-revisions.component';
|
||||
import { HealthCheckComponent } from './health-check/health-check.component';
|
||||
import { SearchComponent } from './search/search.component';
|
||||
|
||||
export const appRoutes: Routes = [
|
||||
@@ -31,6 +32,7 @@ export const appRoutes: Routes = [
|
||||
{ path: 'changePassword', component: ChangePasswordComponent, canActivate: [AuthGuard] },
|
||||
{ path: 'profilEdit', component: ProfilEditionComponent, canActivate: [AuthGuard] },
|
||||
{ path: 'versionrevisions', component: VersionRevisionComponent },
|
||||
{ path: 'healthCheck', component: HealthCheckComponent },
|
||||
{ path: '', redirectTo: '/home', pathMatch: 'full' },
|
||||
{ path: '**', redirectTo: '/home' }
|
||||
];
|
||||
|
||||
@@ -73,3 +73,11 @@ export class VersionRevision {
|
||||
public bugfix: boolean
|
||||
) { }
|
||||
}
|
||||
|
||||
export class Metrics {
|
||||
constructor(
|
||||
public version: String,
|
||||
public uptime: Date,
|
||||
public platform: String
|
||||
) { }
|
||||
}
|
||||
|
||||
@@ -7,6 +7,13 @@
|
||||
<i id="appVersion" routerLink="/versionrevisions" mdbTooltip="Notes de versions" placement="top" mdbRippleRadius>
|
||||
{{appVersion}}
|
||||
</i>
|
||||
<i id="healthCheck"
|
||||
class="fa fa-heartbeat"
|
||||
routerLink="/healthCheck"
|
||||
mdbTooltip="Ligne de vie"
|
||||
placement="top"
|
||||
mdbRippleRadius>
|
||||
</i>
|
||||
</span>
|
||||
<span class="float-right">
|
||||
<a target="_blank" href="./assets/doc/codiki_user_manual.pdf" mdbTooltip="Manuel d'utilisation" placement="top" mdbRippleRadius>
|
||||
|
||||
@@ -23,3 +23,8 @@ span.anticopy {
|
||||
display: inline;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
#healthCheck {
|
||||
margin-left: 10px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
23
src/main/ts/src/app/health-check/health-check.component.html
Normal file
23
src/main/ts/src/app/health-check/health-check.component.html
Normal file
@@ -0,0 +1,23 @@
|
||||
<div class="col-md-8 offset-md-2 col-lg-6 offset-lg-3">
|
||||
<mdb-card>
|
||||
<!--Card content-->
|
||||
<mdb-card-body>
|
||||
<!--Title-->
|
||||
<mdb-card-title>
|
||||
<h4>Status du serveur</h4>
|
||||
</mdb-card-title>
|
||||
|
||||
<ul class="list-group list-group-flush">
|
||||
<li class="list-group-item">
|
||||
Plateforme <mdb-badge pill="true" primary="true" class="float-right">{{metrics.platform}}</mdb-badge>
|
||||
</li>
|
||||
<li class="list-group-item">
|
||||
Version <mdb-badge pill="true" primary="true" class="float-right">{{metrics.version}}</mdb-badge>
|
||||
</li>
|
||||
<li class="list-group-item">
|
||||
Démarré depuis {{metrics.uptime}}
|
||||
</li>
|
||||
</ul>
|
||||
</mdb-card-body>
|
||||
</mdb-card>
|
||||
</div>
|
||||
23
src/main/ts/src/app/health-check/health-check.component.ts
Normal file
23
src/main/ts/src/app/health-check/health-check.component.ts
Normal file
@@ -0,0 +1,23 @@
|
||||
import { HealthCheckService } from './health-check.service';
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
import { Metrics } from '../core/entities';
|
||||
|
||||
@Component({
|
||||
selector: 'app-health-check',
|
||||
templateUrl: './health-check.component.html',
|
||||
styleUrls: ['./health-check.component.scss']
|
||||
})
|
||||
export class HealthCheckComponent implements OnInit {
|
||||
metrics: Metrics = new Metrics('1.0.0', null, '?');
|
||||
|
||||
constructor(
|
||||
private healthCheckService: HealthCheckService
|
||||
) {}
|
||||
|
||||
ngOnInit() {
|
||||
this.healthCheckService.healthCheck().subscribe(metrics => {
|
||||
this.metrics = metrics;
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
16
src/main/ts/src/app/health-check/health-check.service.ts
Normal file
16
src/main/ts/src/app/health-check/health-check.service.ts
Normal file
@@ -0,0 +1,16 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { HttpClient } from '@angular/common/http';
|
||||
import { Observable } from 'rxjs';
|
||||
import { Metrics } from '../core/entities';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
})
|
||||
export class HealthCheckService {
|
||||
|
||||
constructor(private http: HttpClient) {}
|
||||
|
||||
healthCheck(): Observable<Metrics> {
|
||||
return this.http.get<Metrics>('/api/metrics/healthCheck');
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { HttpClient, HttpHeaders } from '@angular/common/http';
|
||||
import { HttpClient } from '@angular/common/http';
|
||||
import { Observable } from 'rxjs';
|
||||
import { Post } from '../core/entities';
|
||||
|
||||
|
||||
Reference in New Issue
Block a user