update dockerfile
Signed-off-by: Peter Siegmund <developer@mars3142.org>
This commit is contained in:
35
Dockerfile
35
Dockerfile
@@ -1,4 +1,33 @@
|
|||||||
FROM eclipse-temurin:21-jre
|
FROM maven:3-eclipse-temurin-21-jammy as build
|
||||||
COPY target/*.jar app.jar
|
|
||||||
|
# Install nodejs
|
||||||
|
RUN apt-get update
|
||||||
|
RUN apt-get install -y ca-certificates curl gnupg
|
||||||
|
RUN mkdir -p /etc/apt/keyrings
|
||||||
|
RUN curl -fsSL https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key | gpg --dearmor -o /etc/apt/keyrings/nodesource.gpg
|
||||||
|
RUN echo "deb [signed-by=/etc/apt/keyrings/nodesource.gpg] https://deb.nodesource.com/node_18.x nodistro main" | tee /etc/apt/sources.list.d/nodesource.list
|
||||||
|
RUN apt-get update
|
||||||
|
RUN apt-get install nodejs -y
|
||||||
|
|
||||||
|
# Stop running as root at this point
|
||||||
|
RUN useradd -m vaadin
|
||||||
|
WORKDIR /usr/src/app/
|
||||||
|
RUN chown vaadin:vaadin /usr/src/app/
|
||||||
|
USER vaadin
|
||||||
|
|
||||||
|
# Copy pom.xml and prefetch dependencies so a repeated build can continue from the next step with existing dependencies
|
||||||
|
COPY --chown=vaadin pom.xml ./
|
||||||
|
|
||||||
|
# Copy all needed project files to a folder
|
||||||
|
COPY --chown=vaadin:vaadin src src
|
||||||
|
|
||||||
|
# Build the production package, assuming that we validated the version before so no need for running tests again
|
||||||
|
RUN mvn clean package -DskipTests -Pproduction --batch-mode
|
||||||
|
|
||||||
|
# Running stage: the part that is used for running the application
|
||||||
|
FROM eclipse-temurin:21-jre-jammy
|
||||||
|
COPY --from=build /usr/src/app/target/*.jar /usr/app/app.jar
|
||||||
|
RUN useradd -m vaadin
|
||||||
|
USER vaadin
|
||||||
EXPOSE 8080
|
EXPOSE 8080
|
||||||
ENTRYPOINT ["java", "-jar", "/app.jar"]
|
CMD java -jar /usr/app/app.jar
|
||||||
|
23
src/main/frontend/index.html
Normal file
23
src/main/frontend/index.html
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<!--
|
||||||
|
This file is auto-generated by Vaadin.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||||
|
<style>
|
||||||
|
body, #outlet {
|
||||||
|
height: 100vh;
|
||||||
|
width: 100%;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<!-- index.ts is included here automatically (either by the dev server or during the build) -->
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<!-- This outlet div is where the views are rendered -->
|
||||||
|
<div id="outlet"></div>
|
||||||
|
</body>
|
||||||
|
</html>
|
@@ -27,7 +27,6 @@ import com.vaadin.flow.theme.lumo.LumoUtility.TextColor;
|
|||||||
import com.vaadin.flow.theme.lumo.LumoUtility.Whitespace;
|
import com.vaadin.flow.theme.lumo.LumoUtility.Whitespace;
|
||||||
import com.vaadin.flow.theme.lumo.LumoUtility.Width;
|
import com.vaadin.flow.theme.lumo.LumoUtility.Width;
|
||||||
import dev.mars3142.fhq.views.checkoutform.CheckoutFormView;
|
import dev.mars3142.fhq.views.checkoutform.CheckoutFormView;
|
||||||
import dev.mars3142.fhq.views.dashboard.DashboardView;
|
|
||||||
import dev.mars3142.fhq.views.myview.MyViewView;
|
import dev.mars3142.fhq.views.myview.MyViewView;
|
||||||
import org.vaadin.lineawesome.LineAwesomeIcon;
|
import org.vaadin.lineawesome.LineAwesomeIcon;
|
||||||
|
|
||||||
@@ -102,8 +101,6 @@ public class MainLayout extends AppLayout {
|
|||||||
|
|
||||||
private MenuItemInfo[] createMenuItems() {
|
private MenuItemInfo[] createMenuItems() {
|
||||||
return new MenuItemInfo[]{ //
|
return new MenuItemInfo[]{ //
|
||||||
new MenuItemInfo("Dashboard", LineAwesomeIcon.CHART_AREA_SOLID.create(), DashboardView.class), //
|
|
||||||
|
|
||||||
new MenuItemInfo("Checkout Form", LineAwesomeIcon.CREDIT_CARD.create(), CheckoutFormView.class), //
|
new MenuItemInfo("Checkout Form", LineAwesomeIcon.CREDIT_CARD.create(), CheckoutFormView.class), //
|
||||||
|
|
||||||
new MenuItemInfo("My View", LineAwesomeIcon.PENCIL_RULER_SOLID.create(), MyViewView.class), //
|
new MenuItemInfo("My View", LineAwesomeIcon.PENCIL_RULER_SOLID.create(), MyViewView.class), //
|
||||||
|
@@ -38,7 +38,7 @@ import java.util.LinkedHashSet;
|
|||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
@PageTitle("Checkout Form")
|
@PageTitle("Checkout Form")
|
||||||
@Route(value = "checkout-form", layout = MainLayout.class)
|
@Route(value = "", layout = MainLayout.class)
|
||||||
public class CheckoutFormView extends Div {
|
public class CheckoutFormView extends Div {
|
||||||
|
|
||||||
private static final Set<String> states = new LinkedHashSet<>();
|
private static final Set<String> states = new LinkedHashSet<>();
|
||||||
|
@@ -1,227 +0,0 @@
|
|||||||
package dev.mars3142.fhq.views.dashboard;
|
|
||||||
|
|
||||||
|
|
||||||
import com.vaadin.flow.component.Component;
|
|
||||||
import com.vaadin.flow.component.board.Board;
|
|
||||||
import com.vaadin.flow.component.charts.Chart;
|
|
||||||
import com.vaadin.flow.component.charts.model.*;
|
|
||||||
import com.vaadin.flow.component.grid.ColumnTextAlign;
|
|
||||||
import com.vaadin.flow.component.grid.Grid;
|
|
||||||
import com.vaadin.flow.component.grid.GridVariant;
|
|
||||||
import com.vaadin.flow.component.html.H2;
|
|
||||||
import com.vaadin.flow.component.html.Main;
|
|
||||||
import com.vaadin.flow.component.html.Span;
|
|
||||||
import com.vaadin.flow.component.icon.Icon;
|
|
||||||
import com.vaadin.flow.component.icon.VaadinIcon;
|
|
||||||
import com.vaadin.flow.component.orderedlayout.FlexComponent;
|
|
||||||
import com.vaadin.flow.component.orderedlayout.HorizontalLayout;
|
|
||||||
import com.vaadin.flow.component.orderedlayout.VerticalLayout;
|
|
||||||
import com.vaadin.flow.component.select.Select;
|
|
||||||
import com.vaadin.flow.data.renderer.ComponentRenderer;
|
|
||||||
import com.vaadin.flow.router.PageTitle;
|
|
||||||
import com.vaadin.flow.router.Route;
|
|
||||||
import com.vaadin.flow.router.RouteAlias;
|
|
||||||
import com.vaadin.flow.theme.lumo.LumoUtility.BoxSizing;
|
|
||||||
import com.vaadin.flow.theme.lumo.LumoUtility.FontSize;
|
|
||||||
import com.vaadin.flow.theme.lumo.LumoUtility.FontWeight;
|
|
||||||
import com.vaadin.flow.theme.lumo.LumoUtility.Margin;
|
|
||||||
import com.vaadin.flow.theme.lumo.LumoUtility.Padding;
|
|
||||||
import com.vaadin.flow.theme.lumo.LumoUtility.TextColor;
|
|
||||||
import dev.mars3142.fhq.views.MainLayout;
|
|
||||||
import dev.mars3142.fhq.views.dashboard.ServiceHealth.Status;
|
|
||||||
|
|
||||||
@PageTitle("Dashboard")
|
|
||||||
@Route(value = "", layout = MainLayout.class)
|
|
||||||
@RouteAlias(value = "", layout = MainLayout.class)
|
|
||||||
public class DashboardView extends Main {
|
|
||||||
|
|
||||||
public DashboardView() {
|
|
||||||
addClassName("dashboard-view");
|
|
||||||
|
|
||||||
Board board = new Board();
|
|
||||||
board.addRow(createHighlight("Current users", "745", 33.7), createHighlight("View events", "54.6k", -112.45),
|
|
||||||
createHighlight("Conversion rate", "18%", 3.9), createHighlight("Custom metric", "-123.45", 0.0));
|
|
||||||
board.addRow(createViewEvents());
|
|
||||||
board.addRow(createServiceHealth(), createResponseTimes());
|
|
||||||
add(board);
|
|
||||||
}
|
|
||||||
|
|
||||||
private Component createHighlight(String title, String value, Double percentage) {
|
|
||||||
VaadinIcon icon = VaadinIcon.ARROW_UP;
|
|
||||||
String prefix = "";
|
|
||||||
String theme = "badge";
|
|
||||||
|
|
||||||
if (percentage == 0) {
|
|
||||||
prefix = "±";
|
|
||||||
} else if (percentage > 0) {
|
|
||||||
prefix = "+";
|
|
||||||
theme += " success";
|
|
||||||
} else if (percentage < 0) {
|
|
||||||
icon = VaadinIcon.ARROW_DOWN;
|
|
||||||
theme += " error";
|
|
||||||
}
|
|
||||||
|
|
||||||
H2 h2 = new H2(title);
|
|
||||||
h2.addClassNames(FontWeight.NORMAL, Margin.NONE, TextColor.SECONDARY, FontSize.XSMALL);
|
|
||||||
|
|
||||||
Span span = new Span(value);
|
|
||||||
span.addClassNames(FontWeight.SEMIBOLD, FontSize.XXXLARGE);
|
|
||||||
|
|
||||||
Icon i = icon.create();
|
|
||||||
i.addClassNames(BoxSizing.BORDER, Padding.XSMALL);
|
|
||||||
|
|
||||||
Span badge = new Span(i, new Span(prefix + percentage.toString()));
|
|
||||||
badge.getElement().getThemeList().add(theme);
|
|
||||||
|
|
||||||
VerticalLayout layout = new VerticalLayout(h2, span, badge);
|
|
||||||
layout.addClassName(Padding.LARGE);
|
|
||||||
layout.setPadding(false);
|
|
||||||
layout.setSpacing(false);
|
|
||||||
return layout;
|
|
||||||
}
|
|
||||||
|
|
||||||
private Component createViewEvents() {
|
|
||||||
// Header
|
|
||||||
Select year = new Select();
|
|
||||||
year.setItems("2011", "2012", "2013", "2014", "2015", "2016", "2017", "2018", "2019", "2020", "2021");
|
|
||||||
year.setValue("2021");
|
|
||||||
year.setWidth("100px");
|
|
||||||
|
|
||||||
HorizontalLayout header = createHeader("View events", "City/month");
|
|
||||||
header.add(year);
|
|
||||||
|
|
||||||
// Chart
|
|
||||||
Chart chart = new Chart(ChartType.AREASPLINE);
|
|
||||||
Configuration conf = chart.getConfiguration();
|
|
||||||
conf.getChart().setStyledMode(true);
|
|
||||||
|
|
||||||
XAxis xAxis = new XAxis();
|
|
||||||
xAxis.setCategories("Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec");
|
|
||||||
conf.addxAxis(xAxis);
|
|
||||||
|
|
||||||
conf.getyAxis().setTitle("Values");
|
|
||||||
|
|
||||||
PlotOptionsAreaspline plotOptions = new PlotOptionsAreaspline();
|
|
||||||
plotOptions.setPointPlacement(PointPlacement.ON);
|
|
||||||
plotOptions.setMarker(new Marker(false));
|
|
||||||
conf.addPlotOptions(plotOptions);
|
|
||||||
|
|
||||||
conf.addSeries(new ListSeries("Berlin", 189, 191, 291, 396, 501, 403, 609, 712, 729, 942, 1044, 1247));
|
|
||||||
conf.addSeries(new ListSeries("London", 138, 246, 248, 348, 352, 353, 463, 573, 778, 779, 885, 887));
|
|
||||||
conf.addSeries(new ListSeries("New York", 65, 65, 166, 171, 293, 302, 308, 317, 427, 429, 535, 636));
|
|
||||||
conf.addSeries(new ListSeries("Tokyo", 0, 11, 17, 123, 130, 142, 248, 349, 452, 454, 458, 462));
|
|
||||||
|
|
||||||
// Add it all together
|
|
||||||
VerticalLayout viewEvents = new VerticalLayout(header, chart);
|
|
||||||
viewEvents.addClassName(Padding.LARGE);
|
|
||||||
viewEvents.setPadding(false);
|
|
||||||
viewEvents.setSpacing(false);
|
|
||||||
viewEvents.getElement().getThemeList().add("spacing-l");
|
|
||||||
return viewEvents;
|
|
||||||
}
|
|
||||||
|
|
||||||
private Component createServiceHealth() {
|
|
||||||
// Header
|
|
||||||
HorizontalLayout header = createHeader("Service health", "Input / output");
|
|
||||||
|
|
||||||
// Grid
|
|
||||||
Grid<ServiceHealth> grid = new Grid();
|
|
||||||
grid.addThemeVariants(GridVariant.LUMO_NO_BORDER);
|
|
||||||
grid.setAllRowsVisible(true);
|
|
||||||
|
|
||||||
grid.addColumn(new ComponentRenderer<>(serviceHealth -> {
|
|
||||||
Span status = new Span();
|
|
||||||
String statusText = getStatusDisplayName(serviceHealth);
|
|
||||||
status.getElement().setAttribute("aria-label", "Status: " + statusText);
|
|
||||||
status.getElement().setAttribute("title", "Status: " + statusText);
|
|
||||||
status.getElement().getThemeList().add(getStatusTheme(serviceHealth));
|
|
||||||
return status;
|
|
||||||
})).setHeader("").setFlexGrow(0).setAutoWidth(true);
|
|
||||||
grid.addColumn(ServiceHealth::getCity).setHeader("City").setFlexGrow(1);
|
|
||||||
grid.addColumn(ServiceHealth::getInput).setHeader("Input").setAutoWidth(true).setTextAlign(ColumnTextAlign.END);
|
|
||||||
grid.addColumn(ServiceHealth::getOutput).setHeader("Output").setAutoWidth(true)
|
|
||||||
.setTextAlign(ColumnTextAlign.END);
|
|
||||||
|
|
||||||
grid.setItems(new ServiceHealth(Status.EXCELLENT, "Münster", 324, 1540),
|
|
||||||
new ServiceHealth(Status.OK, "Cluj-Napoca", 311, 1320),
|
|
||||||
new ServiceHealth(Status.FAILING, "Ciudad Victoria", 300, 1219));
|
|
||||||
|
|
||||||
// Add it all together
|
|
||||||
VerticalLayout serviceHealth = new VerticalLayout(header, grid);
|
|
||||||
serviceHealth.addClassName(Padding.LARGE);
|
|
||||||
serviceHealth.setPadding(false);
|
|
||||||
serviceHealth.setSpacing(false);
|
|
||||||
serviceHealth.getElement().getThemeList().add("spacing-l");
|
|
||||||
return serviceHealth;
|
|
||||||
}
|
|
||||||
|
|
||||||
private Component createResponseTimes() {
|
|
||||||
HorizontalLayout header = createHeader("Response times", "Average across all systems");
|
|
||||||
|
|
||||||
// Chart
|
|
||||||
Chart chart = new Chart(ChartType.PIE);
|
|
||||||
Configuration conf = chart.getConfiguration();
|
|
||||||
conf.getChart().setStyledMode(true);
|
|
||||||
chart.setThemeName("gradient");
|
|
||||||
|
|
||||||
DataSeries series = new DataSeries();
|
|
||||||
series.add(new DataSeriesItem("System 1", 12.5));
|
|
||||||
series.add(new DataSeriesItem("System 2", 12.5));
|
|
||||||
series.add(new DataSeriesItem("System 3", 12.5));
|
|
||||||
series.add(new DataSeriesItem("System 4", 12.5));
|
|
||||||
series.add(new DataSeriesItem("System 5", 12.5));
|
|
||||||
series.add(new DataSeriesItem("System 6", 12.5));
|
|
||||||
conf.addSeries(series);
|
|
||||||
|
|
||||||
// Add it all together
|
|
||||||
VerticalLayout serviceHealth = new VerticalLayout(header, chart);
|
|
||||||
serviceHealth.addClassName(Padding.LARGE);
|
|
||||||
serviceHealth.setPadding(false);
|
|
||||||
serviceHealth.setSpacing(false);
|
|
||||||
serviceHealth.getElement().getThemeList().add("spacing-l");
|
|
||||||
return serviceHealth;
|
|
||||||
}
|
|
||||||
|
|
||||||
private HorizontalLayout createHeader(String title, String subtitle) {
|
|
||||||
H2 h2 = new H2(title);
|
|
||||||
h2.addClassNames(FontSize.XLARGE, Margin.NONE);
|
|
||||||
|
|
||||||
Span span = new Span(subtitle);
|
|
||||||
span.addClassNames(TextColor.SECONDARY, FontSize.XSMALL);
|
|
||||||
|
|
||||||
VerticalLayout column = new VerticalLayout(h2, span);
|
|
||||||
column.setPadding(false);
|
|
||||||
column.setSpacing(false);
|
|
||||||
|
|
||||||
HorizontalLayout header = new HorizontalLayout(column);
|
|
||||||
header.setJustifyContentMode(FlexComponent.JustifyContentMode.BETWEEN);
|
|
||||||
header.setSpacing(false);
|
|
||||||
header.setWidthFull();
|
|
||||||
return header;
|
|
||||||
}
|
|
||||||
|
|
||||||
private String getStatusDisplayName(ServiceHealth serviceHealth) {
|
|
||||||
Status status = serviceHealth.getStatus();
|
|
||||||
if (status == Status.OK) {
|
|
||||||
return "Ok";
|
|
||||||
} else if (status == Status.FAILING) {
|
|
||||||
return "Failing";
|
|
||||||
} else if (status == Status.EXCELLENT) {
|
|
||||||
return "Excellent";
|
|
||||||
} else {
|
|
||||||
return status.toString();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private String getStatusTheme(ServiceHealth serviceHealth) {
|
|
||||||
Status status = serviceHealth.getStatus();
|
|
||||||
String theme = "badge primary small";
|
|
||||||
if (status == Status.EXCELLENT) {
|
|
||||||
theme += " success";
|
|
||||||
} else if (status == Status.FAILING) {
|
|
||||||
theme += " error";
|
|
||||||
}
|
|
||||||
return theme;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@@ -1,65 +0,0 @@
|
|||||||
package dev.mars3142.fhq.views.dashboard;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Simple DTO class for the inbox list to demonstrate complex object data
|
|
||||||
*/
|
|
||||||
public class ServiceHealth {
|
|
||||||
|
|
||||||
private Status status;
|
|
||||||
|
|
||||||
private String city;
|
|
||||||
|
|
||||||
private int input;
|
|
||||||
|
|
||||||
private int output;
|
|
||||||
|
|
||||||
private String theme;
|
|
||||||
|
|
||||||
enum Status {
|
|
||||||
EXCELLENT, OK, FAILING;
|
|
||||||
}
|
|
||||||
|
|
||||||
public ServiceHealth() {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public ServiceHealth(Status status, String city, int input, int output) {
|
|
||||||
this.status = status;
|
|
||||||
this.city = city;
|
|
||||||
this.input = input;
|
|
||||||
this.output = output;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Status getStatus() {
|
|
||||||
return status;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setStatus(Status status) {
|
|
||||||
this.status = status;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getCity() {
|
|
||||||
return city;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setCity(String city) {
|
|
||||||
this.city = city;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getInput() {
|
|
||||||
return input;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setInput(int input) {
|
|
||||||
this.input = input;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getOutput() {
|
|
||||||
return output;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setOutput(int output) {
|
|
||||||
this.output = output;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
Reference in New Issue
Block a user