Verified Commit 01d4324a authored by Jean-Baptiste Nizet's avatar Jean-Baptiste Nizet Committed by Raphaël Flores
Browse files

feat: setup thymeleaf

- remove the frontend project from gradle
- remove the angular filter
- create a basic layout and home page using thymeleaf
parent 8b3c2c74
......@@ -57,12 +57,8 @@ tasks {
val bootJar by getting(BootJar::class) {
archiveName = "${rootProject.name}.jar"
dependsOn(":frontend:assemble")
dependsOn(buildInfo)
into("BOOT-INF/classes/static") {
from("${project(":frontend").projectDir}/dist/frontend")
}
into("BOOT-INF/classes/META-INF") {
from(buildInfo.destinationDir)
}
......@@ -98,6 +94,7 @@ dependencies {
implementation("org.springframework.boot:spring-boot-starter-actuator")
implementation("org.springframework.boot:spring-boot-starter-security")
implementation("org.springframework.cloud:spring-cloud-starter-config")
implementation("org.springframework.boot:spring-boot-starter-thymeleaf")
// Elasticsearch
implementation("org.elasticsearch:elasticsearch:6.6.2")
......
package fr.inra.urgi.faidare.filter;
import com.google.common.base.Charsets;
import com.google.common.io.ByteSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.io.ResourceLoader;
import org.springframework.stereotype.Component;
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.io.InputStream;
import java.util.Arrays;
/**
* Filter that intercepts all request to potential Angular routes
* (ex: /studies/ID) to send back the Angular `index.html` file with a correct
* base href set to the spring server context path.
*
* Potential angular routes are devised by process of elimination:
* - They should be GET requests
* - They should not end with common static file suffixes {@link AngularRouteFilter#STATIC_SUFFIXES}
* - They should not start with API prefixes {@link AngularRouteFilter#API_PREFIXES}
*
* <p>
* Adapted from data-discovery
*
* @author gcornut
*/
@Component
@WebFilter("/*")
public class AngularRouteFilter implements Filter {
private static final String[] API_PREFIXES = {
"/brapi/v1", "/faidare/v1", "/actuator", "/v2/api-docs", "/swagger-resources"
};
private static final String[] STATIC_SUFFIXES = {
".html", ".js", ".css", ".ico", ".png", ".jpg", ".gif", ".eot", ".svg",
".woff2", ".ttf", ".woff", ".md"
};
@Value("${server.servlet.context-path}")
private String serverContextPath;
private final ResourceLoader resourceLoader;
@Autowired
public AngularRouteFilter(ResourceLoader resourceLoader) {
this.resourceLoader = resourceLoader;
}
@Override
public void doFilter(
ServletRequest req,
ServletResponse response,
FilterChain chain
) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) req;
if (isAngularRoute(request)) {
// Angular route
InputStream inputStream = resourceLoader.getResource("classpath:static/index.html").getInputStream();
ByteSource byteSource = new ByteSource() {
@Override
public InputStream openStream() {
return inputStream;
}
};
String content = byteSource.asCharSource(Charsets.UTF_8).read();
String replacedContent = content.replace(
"<base href=\"./\">",
"<base href=\"" + serverContextPath + "/\">"
);
response.getWriter().write(replacedContent);
return;
}
// Otherwise nothing to do
chain.doFilter(request, response);
}
private boolean isAngularRoute(HttpServletRequest request) {
if (!request.getMethod().equals("GET")) {
return false;
}
String fullUri = request.getRequestURI();
String contextPath = request.getContextPath();
String uri = fullUri.substring(contextPath.length());
return !isApiOrStaticResource(uri);
}
private boolean isApiOrStaticResource(String relativePath) {
// Starts with API prefix
return Arrays.stream(API_PREFIXES).anyMatch(relativePath::startsWith)
// or has static file suffix
|| Arrays.stream(STATIC_SUFFIXES).anyMatch(relativePath::endsWith);
}
@Override
public void init(FilterConfig filterConfig) {
// nothing to do
}
@Override
public void destroy() {
// nothing to do
}
}
package fr.inra.urgi.faidare.web;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;
/**
* Controller for the home page, which doesn't display much
* @author JB Nizet
*/
@Controller
@RequestMapping("")
public class HomeController {
@GetMapping
public ModelAndView home() {
return new ModelAndView("index");
}
}
<!DOCTYPE html>
<html
xmlns:th="http://www.thymeleaf.org"
xmlns:biom="http://www.thymeleaf.org"
th:replace="~{layout/main :: layout(title=~{::title}, content=~{::main})}"
>
<head>
<title>Faidare</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
</head>
<body>
<main>
<h1>Welcome to Faidare</h1>
</main>
</body>
</html>
<!DOCTYPE html>
<html lang="fr" th:fragment="layout (title, content)" xmlns:th="http://www.thymeleaf.org">
<head>
<title th:replace="${title}">Layout Title</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<meta content="width=device-width, initial-scale=1" name="viewport" />
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.0/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-KyZXEAg3QhqLMpG8r+8fhAXLRk2vvoC2f3B09zVXn8CA5QIVfZOJ3BCsw2P0p/We" crossorigin="anonymous">
<link rel="shortcut icon" th:href="@{/static/assets/images/favicon.ico}" type="image/x-icon" />
</head>
<body>
<div class="container">
<header>
Common header
</header>
<div th:replace="${content}">
<p>Layout content</p>
</div>
<footer>
common footer
</footer>
</div>
</body>
</html>
rootProject.name = "faidare"
include("backend", "frontend")
include("backend")
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment