1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
|
package no.eliashaugsbakk.clams.server.config;
import static io.javalin.apibuilder.ApiBuilder.after;
import static io.javalin.apibuilder.ApiBuilder.before;
import static io.javalin.apibuilder.ApiBuilder.delete;
import static io.javalin.apibuilder.ApiBuilder.get;
import static io.javalin.apibuilder.ApiBuilder.path;
import static io.javalin.apibuilder.ApiBuilder.post;
import static io.javalin.apibuilder.ApiBuilder.put;
import io.javalin.apibuilder.EndpointGroup;
import java.util.Map;
public class AppRoutes implements EndpointGroup {
private final AppContext appContext;
public AppRoutes(AppContext appContext) {
this.appContext = appContext;
}
@Override
public void addEndpoints() {
before(ctx -> {
String path = ctx.path();
// Skip non-GET requests, API endpoints, static resources, and queried requests
if (!ctx.method().name().equalsIgnoreCase("GET")
|| path.startsWith("/api")
|| isStaticResource(path)
|| ctx.queryString() != null) {
return;
}
String cachedHtml = appContext.pageCache().get(path);
if (cachedHtml != null) {
ctx.result(cachedHtml);
ctx.contentType("text/html");
ctx.skipRemainingHandlers();
}
});
after(ctx -> {
String method = ctx.method().name();
String path = ctx.path();
// Invalidate cache on any modifying request if successful
if ((method.equalsIgnoreCase("POST")
|| method.equalsIgnoreCase("PUT")
|| method.equalsIgnoreCase("DELETE"))
&& ctx.status().getCode() >= 200 && ctx.status().getCode() < 300) {
appContext.pageCache().clear();
return;
}
// Cache GET responses for HTML pages
String contentType = ctx.contentType();
if (method.equalsIgnoreCase("GET")
&& !path.startsWith("/api") // exclude API responses
&& !isStaticResource(path) // exclude static assets
&& ctx.queryString() == null // exclude queried pages
&& ctx.status().getCode() == 200
&& contentType != null
&& contentType.contains("text/html")) { // only cache HTML responses
String renderedHtml = ctx.result();
if (renderedHtml != null && !renderedHtml.isBlank()) {
appContext.pageCache().put(path, renderedHtml);
}
}
});
get("/", ctx -> ctx.redirect("/home"));
get("/home", ctx -> ctx.render("templates/home.html",
Map.of("page_title", "Elias Haugsbakk", "page_css", "home")));
path("posts", () -> {
get(appContext.getPostsController()::handleGetPosts);
get("{slug}", appContext.getPostsController()::handleGetPost);
});
path("projects", () -> get(appContext.getProjectsController()::handleGetProjects));
path("api", () -> {
before("*", ctx -> {
String authHeader = ctx.header("Authorization");
if (authHeader == null || !authHeader.startsWith("Bearer ")) {
ctx.status(401).json(Map.of("error", "Unauthorized", "message",
"Missing or malformed Authorization header."));
ctx.skipRemainingHandlers();
return;
}
String token = authHeader.substring(7).trim();
if (!appContext.getAuthService().isValid(token)) {
ctx.status(403)
.json(Map.of("error", "Forbidden", "message", "Invalid API validation token."));
ctx.skipRemainingHandlers();
}
});
post("posts", appContext.getPostController()::handlePostPost);
put("posts/{slug}", appContext.getPostController()::handlePutPost);
delete("posts/{slug}", appContext.getPostController()::handleDeletePost);
get("projects", appContext.getProjectsController()::handleGetProjectsApi);
post("projects", appContext.getProjectsController()::handlePostProject);
put("projects/{id}", appContext.getProjectsController()::handlePutProject);
delete("projects/{id}", appContext.getProjectsController()::handleDeleteProject);
get("media", appContext.getMediaController()::handleGetMediaIndex);
get("media/{uuid}", appContext.getMediaController()::handleGetMedia);
post("media", appContext.getMediaController()::handlePostMedia);
delete("media/{uuid}", appContext.getMediaController()::handleDeleteMedia);
});
}
private static boolean isStaticResource(String path) {
if (path == null) {
return false;
}
return path.startsWith("/css/")
|| path.startsWith("/images/");
}
}
|