summaryrefslogtreecommitdiff
path: root/clams-server/src/main/java/no/eliashaugsbakk/clams/server/config/AppRoutes.java
blob: 8de72fdc993a887fdf406aa7982fab8fa0973208 (plain)
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/");
  }
}