diff --git a/keystore.p12 b/keystore.p12
old mode 100755
new mode 100644
index 3ea1242..1296fc2
Binary files a/keystore.p12 and b/keystore.p12 differ
diff --git a/pom.xml b/pom.xml
index e683c45..41f9d50 100755
--- a/pom.xml
+++ b/pom.xml
@@ -5,7 +5,7 @@
org.codiki
codiki
- 1.0.1
+ 1.1.0
jar
codiki
diff --git a/src/main/java/org/codiki/account/AccountController.java b/src/main/java/org/codiki/account/AccountController.java
index 15ed7e0..c44870c 100755
--- a/src/main/java/org/codiki/account/AccountController.java
+++ b/src/main/java/org/codiki/account/AccountController.java
@@ -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")
diff --git a/src/main/java/org/codiki/core/config/CoreConfig.java b/src/main/java/org/codiki/core/config/CoreConfig.java
new file mode 100644
index 0000000..a2f181f
--- /dev/null
+++ b/src/main/java/org/codiki/core/config/CoreConfig.java
@@ -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();
+ }
+}
diff --git a/src/main/java/org/codiki/core/entities/dto/Metrics.java b/src/main/java/org/codiki/core/entities/dto/Metrics.java
new file mode 100644
index 0000000..02393ab
--- /dev/null
+++ b/src/main/java/org/codiki/core/entities/dto/Metrics.java
@@ -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;
+ }
+}
diff --git a/src/main/java/org/codiki/metrics/MetricsController.java b/src/main/java/org/codiki/metrics/MetricsController.java
new file mode 100644
index 0000000..910c5e4
--- /dev/null
+++ b/src/main/java/org/codiki/metrics/MetricsController.java
@@ -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();
+ }
+}
diff --git a/src/main/java/org/codiki/metrics/MetricsService.java b/src/main/java/org/codiki/metrics/MetricsService.java
new file mode 100644
index 0000000..94a5e93
--- /dev/null
+++ b/src/main/java/org/codiki/metrics/MetricsService.java
@@ -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;
+ }
+}
diff --git a/src/main/java/org/codiki/posts/PostController.java b/src/main/java/org/codiki/posts/PostController.java
index 9ca880c..6110cef 100755
--- a/src/main/java/org/codiki/posts/PostController.java
+++ b/src/main/java/org/codiki/posts/PostController.java
@@ -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 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 = postRepository.getByKey(pPostKey);
- if(post.isPresent()) {
- result = post.get();
- } else {
- response.setStatus(HttpServletResponse.SC_NOT_FOUND);
- }
-
- return result;
+ return postRepository.getByKey(pPostKey)
+ .orElseGet(() -> {
+ response.setStatus(HttpServletResponse.SC_NOT_FOUND);
+ 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 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("/")
diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml
index 654b71d..25418f2 100644
--- a/src/main/resources/application.yml
+++ b/src/main/resources/application.yml
@@ -1,6 +1,8 @@
app:
name: Codiki
description: A wiki application.
+ version: 1.2.0
+ platform: develop
codiki:
files:
diff --git a/src/main/ts/src/app/app.module.ts b/src/main/ts/src/app/app.module.ts
index 81719e3..8fe9db7 100755
--- a/src/main/ts/src/app/app.module.ts
+++ b/src/main/ts/src/app/app.module.ts
@@ -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]
diff --git a/src/main/ts/src/app/app.routing.ts b/src/main/ts/src/app/app.routing.ts
index 4504b6c..cec34a5 100755
--- a/src/main/ts/src/app/app.routing.ts
+++ b/src/main/ts/src/app/app.routing.ts
@@ -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' }
];
diff --git a/src/main/ts/src/app/core/entities.ts b/src/main/ts/src/app/core/entities.ts
index 6c7a445..e4b96b3 100755
--- a/src/main/ts/src/app/core/entities.ts
+++ b/src/main/ts/src/app/core/entities.ts
@@ -73,3 +73,11 @@ export class VersionRevision {
public bugfix: boolean
) { }
}
+
+export class Metrics {
+ constructor(
+ public version: String,
+ public uptime: Date,
+ public platform: String
+ ) { }
+}
diff --git a/src/main/ts/src/app/footer/footer.component.html b/src/main/ts/src/app/footer/footer.component.html
index 0ff665c..6377b3e 100644
--- a/src/main/ts/src/app/footer/footer.component.html
+++ b/src/main/ts/src/app/footer/footer.component.html
@@ -7,6 +7,13 @@
{{appVersion}}
+
+
diff --git a/src/main/ts/src/app/footer/footer.component.scss b/src/main/ts/src/app/footer/footer.component.scss
index 3004449..c29f09d 100644
--- a/src/main/ts/src/app/footer/footer.component.scss
+++ b/src/main/ts/src/app/footer/footer.component.scss
@@ -23,3 +23,8 @@ span.anticopy {
display: inline;
cursor: pointer;
}
+
+#healthCheck {
+ margin-left: 10px;
+ cursor: pointer;
+}
diff --git a/src/main/ts/src/app/health-check/health-check.component.html b/src/main/ts/src/app/health-check/health-check.component.html
new file mode 100644
index 0000000..5c986d6
--- /dev/null
+++ b/src/main/ts/src/app/health-check/health-check.component.html
@@ -0,0 +1,23 @@
+
+
+
+
+
+
+ Status du serveur
+
+
+
+ -
+ Plateforme {{metrics.platform}}
+
+ -
+ Version {{metrics.version}}
+
+ -
+ Démarré depuis {{metrics.uptime}}
+
+
+
+
+
diff --git a/src/main/ts/src/app/health-check/health-check.component.scss b/src/main/ts/src/app/health-check/health-check.component.scss
new file mode 100644
index 0000000..e69de29
diff --git a/src/main/ts/src/app/health-check/health-check.component.ts b/src/main/ts/src/app/health-check/health-check.component.ts
new file mode 100644
index 0000000..c31ce0d
--- /dev/null
+++ b/src/main/ts/src/app/health-check/health-check.component.ts
@@ -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;
+ });
+ }
+
+}
diff --git a/src/main/ts/src/app/health-check/health-check.service.ts b/src/main/ts/src/app/health-check/health-check.service.ts
new file mode 100644
index 0000000..f5d1e68
--- /dev/null
+++ b/src/main/ts/src/app/health-check/health-check.service.ts
@@ -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 {
+ return this.http.get('/api/metrics/healthCheck');
+ }
+}
diff --git a/src/main/ts/src/app/home/home.service.ts b/src/main/ts/src/app/home/home.service.ts
index ea16f7e..1050880 100755
--- a/src/main/ts/src/app/home/home.service.ts
+++ b/src/main/ts/src/app/home/home.service.ts
@@ -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';