diff options
Diffstat (limited to 'clams-server/src/main')
10 files changed, 214 insertions, 34 deletions
diff --git a/clams-server/src/main/java/no/eliashaugsbakk/clams/server/config/AppContext.java b/clams-server/src/main/java/no/eliashaugsbakk/clams/server/config/AppContext.java index 30c22e1..2d52374 100644 --- a/clams-server/src/main/java/no/eliashaugsbakk/clams/server/config/AppContext.java +++ b/clams-server/src/main/java/no/eliashaugsbakk/clams/server/config/AppContext.java @@ -4,10 +4,13 @@ import java.nio.file.Path; import no.eliashaugsbakk.clams.server.controller.PostsController; import no.eliashaugsbakk.clams.server.controller.MediaController; import no.eliashaugsbakk.clams.server.controller.PostController; +import no.eliashaugsbakk.clams.server.controller.ProjectsController; import no.eliashaugsbakk.clams.server.repository.PostsRepo; import no.eliashaugsbakk.clams.server.repository.PostsRepoSqlite; import no.eliashaugsbakk.clams.server.repository.MediaRepo; import no.eliashaugsbakk.clams.server.repository.MediaRepoSqlite; +import no.eliashaugsbakk.clams.server.repository.ProjectsRepo; +import no.eliashaugsbakk.clams.server.repository.ProjectsRepoSqlite; import no.eliashaugsbakk.clams.server.repository.SqliteManager; import no.eliashaugsbakk.clams.server.service.AuthService; import no.eliashaugsbakk.clams.server.service.SlugService; @@ -17,6 +20,7 @@ public class AppContext implements AutoCloseable { private final PostsController postsController; private final MediaController mediaController; private final PostController postController; + private final ProjectsController projectsController; private final AuthService authService; public AppContext() { @@ -29,16 +33,19 @@ public class AppContext implements AutoCloseable { PostsRepo postsRepo = new PostsRepoSqlite(dbManager); MediaRepo mediaRepo = new MediaRepoSqlite(dbManager); + ProjectsRepo projectsRepo = new ProjectsRepoSqlite(dbManager); SlugService slugService = new SlugService(postsRepo); this.postsController = new PostsController(dbManager); this.mediaController = new MediaController(mediaRepo, appConfig); + this.projectsController = new ProjectsController(projectsRepo); this.postController = new PostController(postsRepo, slugService); this.authService = new AuthService(appConfig); } public PostsController getPostsController() { return postsController; } public MediaController getMediaController() { return mediaController; } + public ProjectsController getProjectsController() { return projectsController; } public PostController getPostController() { return postController; } public AuthService getAuthService() { return authService; } diff --git a/clams-server/src/main/java/no/eliashaugsbakk/clams/server/config/AppRoutes.java b/clams-server/src/main/java/no/eliashaugsbakk/clams/server/config/AppRoutes.java index 0157496..abd2926 100644 --- a/clams-server/src/main/java/no/eliashaugsbakk/clams/server/config/AppRoutes.java +++ b/clams-server/src/main/java/no/eliashaugsbakk/clams/server/config/AppRoutes.java @@ -22,14 +22,14 @@ public class AppRoutes implements EndpointGroup { get("/", ctx -> ctx.redirect("/home")); get("/home", ctx -> ctx.render("templates/home.html", Map.of("page_title", "Elias Haugsbakk", "page_css", "home"))); - get("/projects", ctx -> ctx.render("templates/projects.html", - Map.of("page_title", "Projects - Elias Haugsbakk", "page_css", "projects"))); 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"); @@ -54,6 +54,11 @@ public class AppRoutes implements EndpointGroup { 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); diff --git a/clams-server/src/main/java/no/eliashaugsbakk/clams/server/controller/ProjectsController.java b/clams-server/src/main/java/no/eliashaugsbakk/clams/server/controller/ProjectsController.java new file mode 100644 index 0000000..f3e0463 --- /dev/null +++ b/clams-server/src/main/java/no/eliashaugsbakk/clams/server/controller/ProjectsController.java @@ -0,0 +1,44 @@ +package no.eliashaugsbakk.clams.server.controller; + +import io.javalin.http.Context; +import java.util.List; +import java.util.Map; +import no.eliashaugsbakk.clams.server.model.Project; +import no.eliashaugsbakk.clams.server.repository.ProjectsRepo; + +public class ProjectsController { + private final ProjectsRepo projectsRepo; + + public ProjectsController(ProjectsRepo projectsRepo) { + this.projectsRepo = projectsRepo; + } + + public void handleGetProjects(Context ctx) { + List<Project> allProjects = projectsRepo.getAllProjects(); + ctx.render("templates/projects.html", + Map.of( + "page_title", "Blog", + "page_css", "projects", + "projects", allProjects + )); + } + + public void handlePostProject(Context ctx) { + Project newProject = ctx.bodyAsClass(Project.class); + projectsRepo.addProject(newProject); + } + + public void handlePutProject(Context ctx) { + Project updateProject = ctx.bodyAsClass(Project.class); + projectsRepo.updateProject(Long.parseLong(ctx.pathParam("id")), updateProject); + } + + public void handleDeleteProject(Context ctx) { + projectsRepo.deleteProject(Long.parseLong(ctx.pathParam("id"))); + } + + public void handleGetProjectsApi(Context ctx) { + List<Project> allProjects = projectsRepo.getAllProjects(); + ctx.json(allProjects); + } +} diff --git a/clams-server/src/main/java/no/eliashaugsbakk/clams/server/model/Project.java b/clams-server/src/main/java/no/eliashaugsbakk/clams/server/model/Project.java new file mode 100644 index 0000000..fb4e313 --- /dev/null +++ b/clams-server/src/main/java/no/eliashaugsbakk/clams/server/model/Project.java @@ -0,0 +1,4 @@ +package no.eliashaugsbakk.clams.server.model; + +public record Project(long id, String name, String readMoreUrl, String gitUrl, String gitHubUrl, String description) { +} diff --git a/clams-server/src/main/java/no/eliashaugsbakk/clams/server/repository/PostsRepoSqlite.java b/clams-server/src/main/java/no/eliashaugsbakk/clams/server/repository/PostsRepoSqlite.java index 8ed15af..6943d97 100644 --- a/clams-server/src/main/java/no/eliashaugsbakk/clams/server/repository/PostsRepoSqlite.java +++ b/clams-server/src/main/java/no/eliashaugsbakk/clams/server/repository/PostsRepoSqlite.java @@ -187,7 +187,7 @@ public class PostsRepoSqlite implements PostsRepo { stmt.executeUpdate(); } catch (SQLException e) { - throw new RepoException("Error deleting posts post: " + slug, e); + throw new RepoException("Error deleting post " + slug + ": ", e); } } diff --git a/clams-server/src/main/java/no/eliashaugsbakk/clams/server/repository/ProjectsRepo.java b/clams-server/src/main/java/no/eliashaugsbakk/clams/server/repository/ProjectsRepo.java new file mode 100644 index 0000000..6477d68 --- /dev/null +++ b/clams-server/src/main/java/no/eliashaugsbakk/clams/server/repository/ProjectsRepo.java @@ -0,0 +1,12 @@ +package no.eliashaugsbakk.clams.server.repository; + +import java.util.List; +import no.eliashaugsbakk.clams.server.model.Project; + +public interface ProjectsRepo { + void addProject(Project post); + void updateProject(long id, Project project); + void deleteProject(long id); + + List<Project> getAllProjects(); +} diff --git a/clams-server/src/main/java/no/eliashaugsbakk/clams/server/repository/ProjectsRepoSqlite.java b/clams-server/src/main/java/no/eliashaugsbakk/clams/server/repository/ProjectsRepoSqlite.java new file mode 100644 index 0000000..b761286 --- /dev/null +++ b/clams-server/src/main/java/no/eliashaugsbakk/clams/server/repository/ProjectsRepoSqlite.java @@ -0,0 +1,112 @@ +package no.eliashaugsbakk.clams.server.repository; + +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.List; +import no.eliashaugsbakk.clams.server.model.Project; + +public class ProjectsRepoSqlite implements ProjectsRepo { + private final SqliteManager manager; + + public ProjectsRepoSqlite(SqliteManager manager) { + this.manager = manager; + } + + @Override + public void addProject(Project project) { + String sql = """ + INSERT INTO projects (name, read_more_url, git_url, git_hub_url, description) + VALUES (?, ?, ?, ?, ?) + """; + + try (Connection conn = manager.getConnection(); + PreparedStatement stmt = conn.prepareStatement(sql)) { + + stmt.setString(1, project.name()); + stmt.setString(2, project.readMoreUrl()); + stmt.setString(3, project.gitUrl()); + stmt.setString(4, project.gitHubUrl()); + stmt.setString(5, project.description()); + + stmt.executeUpdate(); + } catch (SQLException e) { + throw new RepoException("Error adding project: " + project.name(), e); + } + } + + @Override + public void updateProject(long id, Project project) { + String sql = """ + UPDATE projects + SET name = ?, read_more_url = ?, git_url = ?, git_hub_url = ?, description = ? + WHERE id = ? + """; + + try (Connection conn = manager.getConnection(); + PreparedStatement stmt = conn.prepareStatement(sql)) { + + stmt.setString(1, project.name()); + stmt.setString(2, project.readMoreUrl()); + stmt.setString(3, project.gitUrl()); + stmt.setString(4, project.gitHubUrl()); + stmt.setString(5, project.description()); + stmt.setLong(6, id); + + stmt.executeUpdate(); + } catch (SQLException e) { + throw new RepoException("Error updating project: ", e); + } + } + + @Override + public void deleteProject(long id) { + String sql = """ + DELETE FROM projects + WHERE id = ? + """; + + try (Connection conn = manager.getConnection(); + PreparedStatement stmt = conn.prepareStatement(sql)) { + + stmt.setLong(1, id); + + stmt.executeUpdate(); + + } catch (SQLException e) { + throw new RepoException("Error deleting project " + id + ": ", e); + } + } + + @Override + public List<Project> getAllProjects() { + String sql = """ + SELECT id, name, read_more_url, git_url, git_hub_url, description + FROM projects + """; + + List<Project> projects = new ArrayList<>(); + + try (Connection conn = manager.getConnection(); + PreparedStatement stmt = conn.prepareStatement(sql); + ResultSet resultSet = stmt.executeQuery()) { + + while (resultSet.next()) { + long id = resultSet.getLong("id"); + String name = resultSet.getString("name"); + String read_more_url = resultSet.getString("read_more_url"); + String git_url = resultSet.getString("git_url"); + String git_hub_url = resultSet.getString("git_hub_url"); + String description = resultSet.getString("description"); + + projects.add(new Project(id, name, read_more_url, git_url, git_hub_url, description)); + } + + return projects; + } catch (SQLException e) { + throw new RepoException("Error fetching all projects", e); + } + } +} diff --git a/clams-server/src/main/java/no/eliashaugsbakk/clams/server/repository/SqliteManager.java b/clams-server/src/main/java/no/eliashaugsbakk/clams/server/repository/SqliteManager.java index 898729a..cf4b8ee 100644 --- a/clams-server/src/main/java/no/eliashaugsbakk/clams/server/repository/SqliteManager.java +++ b/clams-server/src/main/java/no/eliashaugsbakk/clams/server/repository/SqliteManager.java @@ -47,10 +47,22 @@ public class SqliteManager implements AutoCloseable { ); """; + String projects = """ + CREATE TABLE IF NOT EXISTS projects ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + name TEXT NOT NULL, + read_more_url TEXT, + git_url TEXT, + git_hub_url TEXT, + description TEXT + ); + """; + try (Connection conn = dataSource.getConnection(); Statement stmt = conn.createStatement()) { stmt.execute(posts); stmt.execute(images); + stmt.execute(projects); } catch (SQLException e) { throw new RepoException("Error while initializing database", e); diff --git a/clams-server/src/main/resources/public/css/common.css b/clams-server/src/main/resources/public/css/common.css index dc81927..91f14b1 100644 --- a/clams-server/src/main/resources/public/css/common.css +++ b/clams-server/src/main/resources/public/css/common.css @@ -107,13 +107,13 @@ a:hover { font-size: 0.8rem; text-decoration: none; display: inline-block; - transition: background-color 0.2s ease, color 0.2s ease; + transition: color ,background-color 0.2s ease, color 0.2s ease; } .pill-button:hover, .pill-button:focus-visible { background: var(--surface-hover-color); - color: var(--text-color); + color: var(--accent-color); } .pill-button:focus-visible { diff --git a/clams-server/src/main/resources/templates/projects.html b/clams-server/src/main/resources/templates/projects.html index 8d476cd..6e31def 100644 --- a/clams-server/src/main/resources/templates/projects.html +++ b/clams-server/src/main/resources/templates/projects.html @@ -9,41 +9,25 @@ <section class="projects-category"> <ul class="project-list"> + {% for project in projects %} <li class="project-item"> <div class="project-meta"> - <h3>Project One Name</h3> - <p>Short project description / introduction.</p> + <h3>{{ project.name }}</h3> + <p>{{ project.description }}</p> </div> <div class="project-actions"> - <a href="#" class="pill-button">Read More</a> - <a href="#" class="pill-button">Self-Hosted Git</a> - <a href="#" class="pill-button">GitHub</a> - </div> - </li> - - <li class="project-item"> - <div class="project-meta"> - <h3>Project Two Name</h3> - <p>Short project description / introduction.</p> - </div> - <div class="project-actions"> - <a href="#" class="pill-button">Read More</a> - <a href="#" class="pill-button">Self-Hosted Git</a> - <a href="#" class="pill-button">GitHub</a> - </div> - </li> - - <li class="project-item"> - <div class="project-meta"> - <h3>Other Project One</h3> - <p>A short one-line description.</p> - </div> - <div class="project-actions"> - <a href="#" class="pill-button">Read More</a> - <a href="#" class="pill-button">Self-Hosted Git</a> - <a href="#" class="pill-button">GitHub</a> + {% if project.readMoreUrl %} + <a href="{{ project.readMoreUrl }}" class="pill-button">Read More</a> + {% endif %} + {% if project.gitUrl %} + <a href="{{ project.gitUrl }}" class="pill-button">Self-Hosted Git</a> + {% endif %} + {% if project.gitHubUrl %} + <a href="{{ project.gitHubUrl }}" class="pill-button">GitHub</a> + {% endif %} </div> </li> + {% endfor %} </ul> </section> </main> |
