diff options
author | William Harrington <kb0iic@berzerkula.org> | 2025-02-11 21:30:34 -0600 |
---|---|---|
committer | William Harrington <kb0iic@berzerkula.org> | 2025-02-11 21:30:34 -0600 |
commit | 5db196eebb04155491630b5396d1b7a7f2eab4e6 (patch) | |
tree | fc53afeb77808ca6c8ed39a055ac79241c756540 /src/main | |
parent | 0adf14e0466bc153f4924cef62b18352aa987827 (diff) |
Push initial release 1.0.0
Diffstat (limited to 'src/main')
28 files changed, 1941 insertions, 0 deletions
diff --git a/src/main/java/org/berzerkula/builddb/BuilddbApplication.java b/src/main/java/org/berzerkula/builddb/BuilddbApplication.java new file mode 100644 index 0000000..7ab9042 --- /dev/null +++ b/src/main/java/org/berzerkula/builddb/BuilddbApplication.java @@ -0,0 +1,19 @@ +package org.berzerkula.builddb; + +import com.ulisesbocchio.jasyptspringboot.annotation.EnableEncryptableProperties; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@EnableEncryptableProperties +@SpringBootApplication +public class BuilddbApplication { + + private static final Logger logger = LoggerFactory.getLogger(BuilddbApplication.class); + + public static void main(String[] args) { + SpringApplication.run(BuilddbApplication.class, args); + } + +} diff --git a/src/main/java/org/berzerkula/builddb/config/SecurityConfig.java b/src/main/java/org/berzerkula/builddb/config/SecurityConfig.java new file mode 100644 index 0000000..dbaacd5 --- /dev/null +++ b/src/main/java/org/berzerkula/builddb/config/SecurityConfig.java @@ -0,0 +1,79 @@ +package org.berzerkula.builddb.config; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; +import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; +import org.springframework.security.crypto.password.PasswordEncoder; +import org.springframework.security.web.SecurityFilterChain; + +@Configuration +@EnableWebSecurity +@EnableMethodSecurity +public class SecurityConfig { + + @Bean + public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { + return http + .authorizeHttpRequests( auth -> auth + .requestMatchers("/").permitAll() + .requestMatchers("/actuator/**").hasRole("admin") + .requestMatchers("/env/**").hasRole("admin") + .requestMatchers("/health/**").hasRole("admin") + .requestMatchers("/info/**").hasRole("admin") + .requestMatchers("/contact").permitAll() + .requestMatchers("/pkgs/**").hasRole("client") + .requestMatchers("/register").permitAll() + .requestMatchers("/login").permitAll() + .requestMatchers("/logout").permitAll() + .anyRequest().authenticated() + ) + .formLogin(form -> form + .loginPage("/login") + .usernameParameter("email") + .passwordParameter("password") + .defaultSuccessUrl("/", true) + ) + .logout(config -> config.logoutSuccessUrl("/")) + .build(); + } + + + @Bean + public PasswordEncoder passwordEncoder() { + return new BCryptPasswordEncoder(); + } +} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/main/java/org/berzerkula/builddb/controllers/AccountController.java b/src/main/java/org/berzerkula/builddb/controllers/AccountController.java new file mode 100644 index 0000000..6cec175 --- /dev/null +++ b/src/main/java/org/berzerkula/builddb/controllers/AccountController.java @@ -0,0 +1,141 @@ +package org.berzerkula.builddb.controllers; + +import java.util.Date; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.core.Authentication; +import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; +import org.springframework.stereotype.Controller; +import org.springframework.ui.Model; +import org.springframework.validation.BindingResult; +import org.springframework.validation.FieldError; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.ModelAttribute; +import org.springframework.web.bind.annotation.PostMapping; + +import org.berzerkula.builddb.models.AppUser; +import org.berzerkula.builddb.models.RegisterDto; +import org.berzerkula.builddb.repositories.AppUserRepository; + +import jakarta.validation.Valid; + +@Controller +public class AccountController { + + @Autowired + private AppUserRepository repo; + + @GetMapping("/profile") + public String profile(Authentication auth, Model model) { + AppUser user = repo.findByEmail(auth.getName()); + model.addAttribute("appUser", user); + + return "profile"; + } + + @GetMapping("/login") + public String login() { + return "login"; + } + + @GetMapping("/register") + public String register(Model model) { + RegisterDto registerDto = new RegisterDto(); + model.addAttribute(registerDto); + model.addAttribute("success", false); + return "register"; + } + + @PostMapping("/register") + public String register( + Model model, + @Valid @ModelAttribute RegisterDto registerDto, + BindingResult result + ) { + + if (!registerDto.getPassword().equals(registerDto.getConfirmPassword())) { + result.addError( + new FieldError("registerDto", "confirmPassword" + , "Password and Confirm Password do not match") + ); + } + + + AppUser appUser = repo.findByEmail(registerDto.getEmail()); + if (appUser != null) { + result.addError( + new FieldError("registerDto", "email" + , "Email address is already used") + ); + } + + + if (result.hasErrors()) { + return "register"; + } + + + try { + // create new account + var bCryptEncoder = new BCryptPasswordEncoder(); + + + AppUser newUser = new AppUser(); + newUser.setFirstName(registerDto.getFirstName()); + newUser.setLastName(registerDto.getLastName()); + newUser.setEmail(registerDto.getEmail()); + newUser.setPhone(registerDto.getPhone()); + newUser.setAddress(registerDto.getAddress()); + newUser.setRole("client"); + newUser.setCreatedAt(new Date()); + newUser.setPassword(bCryptEncoder.encode(registerDto.getPassword())); + + repo.save(newUser); + + + model.addAttribute("registerDto", new RegisterDto()); + model.addAttribute("success", true); + } + catch(Exception ex) { + result.addError( + new FieldError("registerDto", "firstName" + , ex.getMessage()) + ); + } + + return "register"; + } +} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/main/java/org/berzerkula/builddb/controllers/DashboardController.java b/src/main/java/org/berzerkula/builddb/controllers/DashboardController.java new file mode 100644 index 0000000..e928312 --- /dev/null +++ b/src/main/java/org/berzerkula/builddb/controllers/DashboardController.java @@ -0,0 +1,27 @@ +package org.berzerkula.builddb.controllers; + +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.GetMapping; + +@Controller +public class DashboardController { + + @GetMapping("/user") + public String userDashboard() { + return "user"; + } + + @GetMapping("/client") + public String clientDashboard() { + return "client"; + } + + @GetMapping("/admin") + public String adminDashboard() { + return "admin"; + } + + @GetMapping("/actuatorDashboard") + public String actuatorDashboard() { return "actuatorDashboard"; } + +} diff --git a/src/main/java/org/berzerkula/builddb/controllers/HomeController.java b/src/main/java/org/berzerkula/builddb/controllers/HomeController.java new file mode 100644 index 0000000..ad171c8 --- /dev/null +++ b/src/main/java/org/berzerkula/builddb/controllers/HomeController.java @@ -0,0 +1,47 @@ +package org.berzerkula.builddb.controllers; + +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.GetMapping; + +@Controller +public class HomeController { + + @GetMapping("/contact") + public String contact() { + return "contact"; + } + +} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/main/java/org/berzerkula/builddb/controllers/PkgController.java b/src/main/java/org/berzerkula/builddb/controllers/PkgController.java new file mode 100644 index 0000000..404a58b --- /dev/null +++ b/src/main/java/org/berzerkula/builddb/controllers/PkgController.java @@ -0,0 +1,169 @@ +package org.berzerkula.builddb.controllers; + + +import jakarta.persistence.OrderBy; +import jakarta.validation.Valid; +import org.berzerkula.builddb.models.Pkg; +import org.berzerkula.builddb.models.PkgDto; +import org.berzerkula.builddb.repositories.PkgRepository; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.domain.Sort; +import org.springframework.stereotype.Controller; +import org.springframework.ui.Model; +import org.springframework.validation.BindingResult; +import org.springframework.web.bind.annotation.*; + +import java.util.List; +import java.util.Objects; + +@Controller +@RequestMapping("/pkgs") +public class PkgController { + + private static final Logger logger = LoggerFactory.getLogger(PkgController.class); + + @Autowired + private PkgRepository repo; + + @GetMapping({"", "/", "/index"}) + public String showPkgList(Model model, + @RequestParam(defaultValue = "sequence,asc") String[] sort, + @RequestParam(defaultValue = "") String repotable) { + try { + String sortField = sort[0]; + String sortDirection = sort[1]; + + Sort.Direction direction = sortDirection.equals("desc") ? Sort.Direction.DESC : Sort.Direction.ASC; + Sort.Order order = new Sort.Order(direction, sortField); + + List<Pkg> pkgs = repo.findAll(Sort.by(order)); + + model.addAttribute("pkgs", pkgs); + model.addAttribute("sortField", sortField); + model.addAttribute("sortDirection", sortDirection); + model.addAttribute("reverseSortDirection", sortDirection.equals("asc") ? "desc" : "asc"); + + } catch (Exception e) { + logger.error(e.getMessage()); + } + + return "pkgs/index"; + } + + @GetMapping("/add") + public String showAddPkgForm(Model model) { + PkgDto pkgDto = new PkgDto(); + model.addAttribute("pkgDto", pkgDto); + return "pkgs/add"; + } + + @PostMapping("/add") + public String addPkg( + @Valid + @ModelAttribute + PkgDto pkgDto, BindingResult result ) { + + if (result.hasErrors()) { + return "pkgs/add"; + } + + try { + + Pkg pkg = new Pkg(); + pkg.setSequence(pkgDto.getSequence()); + pkg.setName(pkgDto.getName()); + pkg.setVersion(pkgDto.getVersion()); + pkg.setConfigure(pkgDto.getConfigure()); + pkg.setBuild(pkgDto.getBuild()); + pkg.setInstall(pkgDto.getInstall()); + pkg.setSetup(pkgDto.getSetup()); + pkg.setNotes(pkgDto.getNotes()); + pkg.setUrl(pkgDto.getUrl()); + + repo.save(pkg); + + } catch (Exception e) { + logger.error(e.getMessage()); + } + + return "redirect:/pkgs"; + } + + @GetMapping("/edit") + public String showEditPkgForm(Model model, @RequestParam int id) { + + try { + Pkg pkg = repo.findById(id).get(); + model.addAttribute("pkg", pkg); + + PkgDto pkgDto = new PkgDto(); + pkgDto.setSequence(pkg.getSequence()); + pkgDto.setName(pkg.getName()); + pkgDto.setVersion(pkg.getVersion()); + pkgDto.setConfigure(pkg.getConfigure()); + pkgDto.setBuild(pkg.getBuild()); + pkgDto.setInstall(pkg.getInstall()); + pkgDto.setSetup(pkg.getSetup()); + pkgDto.setNotes(pkg.getNotes()); + pkgDto.setUrl(pkg.getUrl()); + + model.addAttribute("pkgDto", pkgDto); + + } catch(Exception ex) { + logger.error("Exception: {}", ex.getMessage()); + } + + return "pkgs/edit"; + } + + @PostMapping("/edit") + public String editPkg( + Model model, + @RequestParam int id, + @Valid + @ModelAttribute PkgDto pkgDto, BindingResult result ) { + + try { + Pkg pkg = repo.findById(id).get(); + model.addAttribute("pkg", pkg); + + if (result.hasErrors()) { + return "pkgs/edit"; + } + + pkg.setSequence(pkgDto.getSequence()); + pkg.setName(pkgDto.getName()); + pkg.setVersion(pkgDto.getVersion()); + pkg.setConfigure(pkgDto.getConfigure()); + pkg.setBuild(pkgDto.getBuild()); + pkg.setInstall(pkgDto.getInstall()); + pkg.setSetup(pkgDto.getSetup()); + pkg.setNotes(pkgDto.getNotes()); + pkg.setUrl(pkgDto.getUrl()); + + repo.save(pkg); + + } catch(Exception ex) { + logger.error("Exception: {}", ex.getMessage()); + } + return "redirect:/pkgs/#" + id; + + } + + @GetMapping("/delete") + public String deletePkg(@RequestParam int id) { + + try { + Pkg pkg = repo.findById(id).get(); + + repo.delete(pkg); + + } catch (Exception ex) { + logger.error("Exception: {}", ex.getMessage()); + } + + return "redirect:/pkgs/"; + } +} diff --git a/src/main/java/org/berzerkula/builddb/models/AppUser.java b/src/main/java/org/berzerkula/builddb/models/AppUser.java new file mode 100644 index 0000000..21c5a33 --- /dev/null +++ b/src/main/java/org/berzerkula/builddb/models/AppUser.java @@ -0,0 +1,107 @@ +package org.berzerkula.builddb.models; + +import jakarta.persistence.*; + +import java.util.Date; + +@Entity +@Table(name="users") +public class AppUser { + + @Id + @GeneratedValue(strategy=GenerationType.IDENTITY) + private int id; + + private String firstName; + private String lastName; + + @Column(unique = true, nullable = false) + private String email; + + private String phone; + private String address; + private String password; + private String role; + private Date createdAt; + + public AppUser(String firstName, String lastName, String email, String phone, String address, + String password, String role) { + this.firstName = firstName; + this.lastName = lastName; + this.email = email; + this.password = password; + this.role = role; + this.createdAt = new Date(); + } + + public AppUser() {} + + public int getId() { + return id; + } + + public String getFirstName() { + return firstName; + } + + public void setFirstName(String firstName) { + this.firstName = firstName; + } + + public String getLastName() { + return lastName; + } + + public void setLastName(String lastName) { + this.lastName = lastName; + } + + public String getEmail() { + return email; + } + + public void setEmail(String email) { + this.email = email; + } + + public String getPhone() { + return phone; + } + + public void setPhone(String phone) { + this.phone = phone; + } + + public String getAddress() { + return address; + } + + public void setAddress(String address) { + this.address = address; + } + + public String getPassword() { + return password; + } + + public void setPassword(String password) { + this.password = password; + } + + public String getRole() { + return role; + } + + public void setRole(String role) { + this.role = role; + } + + public Date getCreatedAt() { + return createdAt; + } + + public void setCreatedAt(Date createdAt) { + this.createdAt = createdAt; + } + +} diff --git a/src/main/java/org/berzerkula/builddb/models/Pkg.java b/src/main/java/org/berzerkula/builddb/models/Pkg.java new file mode 100644 index 0000000..5ce80ba --- /dev/null +++ b/src/main/java/org/berzerkula/builddb/models/Pkg.java @@ -0,0 +1,118 @@ +package org.berzerkula.builddb.models; + +import jakarta.persistence.*; + +@Entity +@Table(name = "rock5b_srv_bld") +public class Pkg { + @Id + @GeneratedValue(strategy= GenerationType.IDENTITY) + private int id; + + private String name; + private Integer sequence; + private String version; + @Column(columnDefinition = "TEXT") + private String configure; + @Column(columnDefinition = "TEXT") + private String build; + @Column(columnDefinition = "TEXT") + private String install; + @Column(columnDefinition = "TEXT") + private String setup; + @Column(columnDefinition = "TEXT") + private String notes; + @Column(columnDefinition = "TEXT") + private String url; + + public Pkg(Integer sequence, String name, String version, String configure, String build, String install, + String setup, String notes, String url) { + this.sequence = sequence; + this.name = name; + this.version = version; + this.configure = configure; + this.build = build; + this.install = install; + this.setup = setup; + this.notes = notes; + this.url = url; + } + + public Pkg() {} + + public int getId() { + return id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public Integer getSequence() { + return sequence; + } + + public void setSequence(Integer seq) { + this.sequence = seq; + } + + public String getVersion() { + return version; + } + + public void setVersion(String version) { + this.version = version; + } + + public String getConfigure() { + return configure; + } + + public void setConfigure(String configure) { + this.configure = configure; + } + + public String getBuild() { + return build; + } + + public void setBuild(String build) { + this.build = build; + } + + public String getInstall() { + return install; + } + + public void setInstall(String install) { + this.install = install; + } + + public String getSetup() { + return setup; + } + + public void setSetup(String setup) { + this.setup = setup; + } + + public String getNotes() { + return notes; + } + + public void setNotes(String notes) { + this.notes = notes; + } + + public String getUrl() { + return url; + } + + public void setUrl(String url) { + this.url = url; + } +} diff --git a/src/main/java/org/berzerkula/builddb/models/PkgDto.java b/src/main/java/org/berzerkula/builddb/models/PkgDto.java new file mode 100644 index 0000000..04c47ea --- /dev/null +++ b/src/main/java/org/berzerkula/builddb/models/PkgDto.java @@ -0,0 +1,104 @@ +package org.berzerkula.builddb.models; + +import jakarta.validation.constraints.Digits; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import org.springframework.data.annotation.ReadOnlyProperty; + +public class PkgDto { + @ReadOnlyProperty + private Integer id; + + @NotNull(message = "Required") + @Digits(integer = 4, fraction = 0) + private Integer sequence; + + @NotEmpty(message = "Required") + private String name; + + @NotEmpty(message = "Required") + private String version; + + private String configure; + private String build; + private String install; + private String setup; + private String notes; + private String url; + + public Integer getSequence() { + return sequence; + } + + public void setSequence(Integer sequence) { + this.sequence = sequence; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getVersion() { + return version; + } + + public void setVersion(String version) { + this.version = version; + } + + public String getConfigure() { + return configure; + } + + public void setConfigure(String configure) { + this.configure = configure; + } + + public String getBuild() { + return build; + } + + public void setBuild(String build) { + this.build = build; + } + + public String getInstall() { + return install; + } + + public void setInstall(String install) { + this.install = install; + } + + public String getSetup() { + return setup; + } + + public void setSetup(String setup) { + this.setup = setup; + } + + public String getNotes() { + return notes; + } + + public void setNotes(String notes) { + this.notes = notes; + } + + public Integer getId() { + return id; + } + + public String getUrl() { + return url; + } + + public void setUrl(String url) { + this.url = url; + } +} diff --git a/src/main/java/org/berzerkula/builddb/models/RegisterDto.java b/src/main/java/org/berzerkula/builddb/models/RegisterDto.java new file mode 100644 index 0000000..eebf014 --- /dev/null +++ b/src/main/java/org/berzerkula/builddb/models/RegisterDto.java @@ -0,0 +1,84 @@ +package org.berzerkula.builddb.models; + +import jakarta.validation.constraints.*; + +public class RegisterDto { + + @NotEmpty + private String firstName; + + @NotEmpty + private String lastName; + + @NotEmpty + @Email + private String email; + + private String phone; + + private String address; + + @Size(min = 6, message = "Minimum Password length is 6 characters") + private String password; + + private String confirmPassword; + + + public String getFirstName() { + return firstName; + } + + public void setFirstName(String firstName) { + this.firstName = firstName; + } + + public String getLastName() { + return lastName; + } + + public void setLastName(String lastName) { + this.lastName = lastName; + } + + public String getEmail() { + return email; + } + + public void setEmail(String email) { + this.email = email; + } + + public String getPhone() { + return phone; + } + + public void setPhone(String phone) { + this.phone = phone; + } + + public String getAddress() { + return address; + } + + public void setAddress(String address) { + this.address = address; + } + + public String getPassword() { + return password; + } + + public void setPassword(String password) { + this.password = password; + } + + public String getConfirmPassword() { + return confirmPassword; + } + + public void setConfirmPassword(String confirmPassword) { + this.confirmPassword = confirmPassword; + } + + +} diff --git a/src/main/java/org/berzerkula/builddb/repositories/AppUserRepository.java b/src/main/java/org/berzerkula/builddb/repositories/AppUserRepository.java new file mode 100644 index 0000000..756d56a --- /dev/null +++ b/src/main/java/org/berzerkula/builddb/repositories/AppUserRepository.java @@ -0,0 +1,10 @@ +package org.berzerkula.builddb.repositories; + +import org.springframework.data.jpa.repository.JpaRepository; + +import org.berzerkula.builddb.models.AppUser; + +public interface AppUserRepository extends JpaRepository<AppUser, Integer> { + + public AppUser findByEmail(String email); +} diff --git a/src/main/java/org/berzerkula/builddb/repositories/PkgRepository.java b/src/main/java/org/berzerkula/builddb/repositories/PkgRepository.java new file mode 100644 index 0000000..30e80eb --- /dev/null +++ b/src/main/java/org/berzerkula/builddb/repositories/PkgRepository.java @@ -0,0 +1,8 @@ +package org.berzerkula.builddb.repositories; + +import org.berzerkula.builddb.models.Pkg; +import org.springframework.data.jpa.repository.JpaRepository; + +public interface PkgRepository extends JpaRepository<Pkg, Integer> { + +} diff --git a/src/main/java/org/berzerkula/builddb/services/AppUserService.java b/src/main/java/org/berzerkula/builddb/services/AppUserService.java new file mode 100644 index 0000000..02cc89f --- /dev/null +++ b/src/main/java/org/berzerkula/builddb/services/AppUserService.java @@ -0,0 +1,36 @@ +package org.berzerkula.builddb.services; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.core.userdetails.User; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.security.core.userdetails.UserDetailsService; +import org.springframework.security.core.userdetails.UsernameNotFoundException; +import org.springframework.stereotype.Service; + +import org.berzerkula.builddb.models.AppUser; +import org.berzerkula.builddb.repositories.AppUserRepository; + +@Service +public class AppUserService implements UserDetailsService { + @Autowired + private AppUserRepository repo; + + @Override + public UserDetails loadUserByUsername(String email) throws UsernameNotFoundException { + AppUser appUser = repo.findByEmail(email); + + + if (appUser != null) { + var springUser = User.withUsername(appUser.getEmail()) + .password(appUser.getPassword()) + .roles(appUser.getRole()) + .build(); + + return springUser; + } + + + return null; + } + +} diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties new file mode 100644 index 0000000..f6a7814 --- /dev/null +++ b/src/main/resources/application.properties @@ -0,0 +1,15 @@ +spring.application.name=builddb + +spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver +spring.datasource.url=jdbc:mysql://nucleus.berzerkula.org:3306/builddb_users +spring.datasource.username=kb0iic +spring.datasource.password=ENC(1xjvlowHDUgzAYCjEEaYcSQOQHL2SHo8) + +spring.jpa.show-sql=true +spring.jpa.hibernate.ddl-auto=update + +jasypt.encryptor.algorithm=PBEWithMD5AndDES +jasypt.encryptor.iv-generator-classname=org.jasypt.iv.NoIvGenerator +jasypt.encryptor.password=builddb + +management.endpoints.web.exposure.include=*
\ No newline at end of file diff --git a/src/main/resources/templates/actuatorDashboard.html b/src/main/resources/templates/actuatorDashboard.html new file mode 100644 index 0000000..e97bb8f --- /dev/null +++ b/src/main/resources/templates/actuatorDashboard.html @@ -0,0 +1,39 @@ +<!DOCTYPE html> +<html xmlns:th="http://www.thymeleaf.org"> +<head> + <title>spring-boot-actuator</title> + <meta charset="UTF-8" /> + <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-QWTKZyjpPEjISv5WaRU9OFeRpok6YctnYmDr5pNlyT2bRjXh0JMhjY6hW+ALEwIH" crossorigin="anonymous"> +</head> +<body> +<div id="app" class="container"> + <nav class="navbar navbar-expand-lg fixed-top bg-body-tertiary border-bottom"> + <div class="container"> + <button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation"> + <span class="navbar-toggler-icon"></span> + </button> + <div class="collapse navbar-collapse" id="navbarSupportedContent"> + <a class="navbar-brand" href="/actuatorDashboard">spring-boot-actuator</a> + <ul class="navbar-nav me-auto mb-2 mb-lg-0"> + <li class="nav-link text-dark"><a th:href="@{/actuator/metrics}">/metrics</a></li> + <li class="nav-link text-dark"><a th:href="@{/actuator/env}">/env</a></li> + <li class="nav-link text-dark"><a th:href="@{/actuator/dump}">/dump</a></li> + <li class="nav-link text-dark"><a th:href="@{/actuator/health}">/health</a></li> + <li class="nav-link text-dark"><a th:href="@{/actuator/beans}">/beans</a></li> + <li class="nav-link text-dark"><a th:href="@{/actuator/autoconfig}">/autoconfig</a></li> + <li class="nav-link text-dark"><a th:href="@{/actuator/info}">/info</a></li> + <li class="nav-link text-dark"><a th:href="@{/actuator/shutdown}">/shutdown</a></li> + </ul> + </div> + </div> + </nav> + + <hr> + <hr> + + <a href="/" class="btn btn-link">Home</a> +</div> + +<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js" integrity="sha384-YvpcrYf0tY3lHB60NNkmXc5s9fDVZLESaAA55NDzOxhy9GkcIdslK1eN7N6jIeHz" crossorigin="anonymous"></script> +</body> +</html>
\ No newline at end of file diff --git a/src/main/resources/templates/admin.html b/src/main/resources/templates/admin.html new file mode 100644 index 0000000..16f8f7f --- /dev/null +++ b/src/main/resources/templates/admin.html @@ -0,0 +1,53 @@ +<!doctype html> +<html lang="en"> +<head> + <meta charset="utf-8"> + <meta name="viewport" content="width=device-width, initial-scale=1"> + <title>builddb</title> + <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-QWTKZyjpPEjISv5WaRU9OFeRpok6YctnYmDr5pNlyT2bRjXh0JMhjY6hW+ALEwIH" crossorigin="anonymous"> +</head> +<body> + +<div class="container py-5"> + <div class="rounded border p-4"> + <h2 class="text-center mb-4">Admin Page</h2> + <hr> + <a href="/" class="btn btn-link">Home</a> + </div> +</div> + + +<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js" integrity="sha384-YvpcrYf0tY3lHB60NNkmXc5s9fDVZLESaAA55NDzOxhy9GkcIdslK1eN7N6jIeHz" crossorigin="anonymous"></script> +</body> +</html> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/main/resources/templates/client.html b/src/main/resources/templates/client.html new file mode 100644 index 0000000..487d427 --- /dev/null +++ b/src/main/resources/templates/client.html @@ -0,0 +1,53 @@ +<!doctype html> +<html lang="en"> +<head> + <meta charset="utf-8"> + <meta name="viewport" content="width=device-width, initial-scale=1"> + <title>builddb</title> + <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-QWTKZyjpPEjISv5WaRU9OFeRpok6YctnYmDr5pNlyT2bRjXh0JMhjY6hW+ALEwIH" crossorigin="anonymous"> +</head> +<body> + +<div class="container py-5"> + <div class="rounded border p-4"> + <h2 class="text-center mb-4">Client Page</h2> + <hr> + <a href="/" class="btn btn-link">Home</a> + </div> +</div> + + +<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js" integrity="sha384-YvpcrYf0tY3lHB60NNkmXc5s9fDVZLESaAA55NDzOxhy9GkcIdslK1eN7N6jIeHz" crossorigin="anonymous"></script> +</body> +</html> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/main/resources/templates/contact.html b/src/main/resources/templates/contact.html new file mode 100644 index 0000000..f1c6a3f --- /dev/null +++ b/src/main/resources/templates/contact.html @@ -0,0 +1,53 @@ +<!doctype html> +<html lang="en"> +<head> + <meta charset="utf-8"> + <meta name="viewport" content="width=device-width, initial-scale=1"> + <title>builddb</title> + <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-QWTKZyjpPEjISv5WaRU9OFeRpok6YctnYmDr5pNlyT2bRjXh0JMhjY6hW+ALEwIH" crossorigin="anonymous"> +</head> +<body> + +<div class="container py-5"> + <div class="rounded border p-4"> + <h2 class="text-center mb-4">Contact</h2> + <hr> + <a href="/" class="btn btn-link">Home</a> + </div> +</div> + + +<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js" integrity="sha384-YvpcrYf0tY3lHB60NNkmXc5s9fDVZLESaAA55NDzOxhy9GkcIdslK1eN7N6jIeHz" crossorigin="anonymous"></script> +</body> +</html> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/main/resources/templates/index.html b/src/main/resources/templates/index.html new file mode 100644 index 0000000..b33e45f --- /dev/null +++ b/src/main/resources/templates/index.html @@ -0,0 +1,37 @@ +<!doctype html> +<html lang="en" xmlns:th="http://www.thymeleaf.org" + xmlns:sec="http://www.thymeleaf.org"> +<head> + <meta charset="utf-8"> + <meta name="viewport" content="width=device-width, initial-scale=1"> + <title>builddb</title> + <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-QWTKZyjpPEjISv5WaRU9OFeRpok6YctnYmDr5pNlyT2bRjXh0JMhjY6hW+ALEwIH" crossorigin="anonymous"> + <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css"> +</head> +<body> + +<nav th:insert="~{/navbar :: navigation}"></nav> + +<hr> + +<div class="container py-5"> + <h1 class="text-center">Builddb</h1> + + + + <a class="btn btn-primary me-3" sec:authorize="hasRole('client')" href="/pkgs">Packages</a> +</div> + +<hr> + +<div th:if="${tableError}" + class="alert alert-danger alert-dismissible fade show" role="alert"> + + <strong>Invalid table!</strong> + <button type="button" class="btn-close" data-bs-dismiss="alert" + aria-label="Close"></button> +</div> + +<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js" integrity="sha384-YvpcrYf0tY3lHB60NNkmXc5s9fDVZLESaAA55NDzOxhy9GkcIdslK1eN7N6jIeHz" crossorigin="anonymous"></script> +</body> +</html>
\ No newline at end of file diff --git a/src/main/resources/templates/login.html b/src/main/resources/templates/login.html new file mode 100644 index 0000000..0893fa0 --- /dev/null +++ b/src/main/resources/templates/login.html @@ -0,0 +1,87 @@ +<!doctype html> +<html lang="en" xmlns:th="http://www.thymeleaf.org" + xmlns:sec="http://www.thymeleaf.org"> +<head> + <meta charset="utf-8"> + <meta name="viewport" content="width=device-width, initial-scale=1"> + <title>builddb</title> + <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-QWTKZyjpPEjISv5WaRU9OFeRpok6YctnYmDr5pNlyT2bRjXh0JMhjY6hW+ALEwIH" crossorigin="anonymous"> +</head> +<body> + +<div class="container py-5"> + <div class="mx-auto rounded border p-4" style="width: 400px"> + <h2 class="text-center mb-4">Login</h2> + <hr /> + + + <div th:if="${param.error}" + class="alert alert-danger alert-dismissible fade show" role="alert"> + + <strong>Invalid Email or Password!</strong> + <button type="button" class="btn-close" data-bs-dismiss="alert" + aria-label="Close"></button> + </div> + + <form method="post"> + <input type="hidden" th:name="${_csrf.parameterName}" + th:value="${_csrf.token}" /> + + + <div class="mb-3"> + <label class="form-label">Email</label> + <input class="form-control" name="email" /> + </div> + + <div class="mb-3"> + <label class="form-label">Password</label> + <input class="form-control" type="password" name="password" /> + </div> + + <div class="row mb-3"> + <div class="col d-grid"> + <button type="submit" class="btn btn-primary">Submit</button> + </div> + <div class="col d-grid"> + <a href="/" class="btn btn-outline-primary">Cancel</a> + </div> + </div> + </form> + </div> +</div> + + +<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js" integrity="sha384-YvpcrYf0tY3lHB60NNkmXc5s9fDVZLESaAA55NDzOxhy9GkcIdslK1eN7N6jIeHz" crossorigin="anonymous"></script> +</body> +</html> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/main/resources/templates/navbar.html b/src/main/resources/templates/navbar.html new file mode 100644 index 0000000..0aa60e0 --- /dev/null +++ b/src/main/resources/templates/navbar.html @@ -0,0 +1,77 @@ +<nav th:fragment="navigation" class="navbar navbar-expand-lg fixed-top bg-body-tertiary border-bottom"> + <div class="container"> + <a class="navbar-brand" href="/">builddb</a> + <button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation"> + <span class="navbar-toggler-icon"></span> + </button> + <div class="collapse navbar-collapse" id="navbarSupportedContent"> + <ul class="navbar-nav me-auto mb-2 mb-lg-0"> + <li class="nav-item"> + <a class="nav-link text-dark" href="/">Home</a> + </li> + <li class="nav-item"> + <a class="nav-link text-dark" href="/contact">Contact</a> + </li> + <li class="nav-item"> + <a class="nav-link text-dark" href="/user">User</a> + </li> + <li class="nav-item"> + <a class="nav-link text-dark" sec:authorize="hasRole('client')" href="/client">Client</a> + </li> + <li class="nav-item"> + <a class="nav-link text-dark" sec:authorize="hasRole('admin')" href="/admin">Admin</a> + </li> + <li class="nav-item"> + <a class="nav-link text-dark" sec:authorize="hasRole('admin')" href="/actuatorDashboard">Actuator</a> + </li> + </ul> + + <ul class="navbar-nav me-3" sec:authorize="hasRole('admin')"> + <li class="nav-item dropdown"> + <a class="nav-link dropdown-toggle text-dark" href="#" role="button" data-bs-toggle="dropdown" aria-expanded="false"> + Admin + </a> + <ul class="dropdown-menu"> + <li><a class="dropdown-item" href="/profile">Profile</a></li> + <li><a class="dropdown-item" href="/">Home</a></li> + </ul> + </li> + </ul> + + <ul class="navbar-nav me-3" sec:authorize="hasRole('client')"> + <li class="nav-item dropdown"> + <a class="nav-link dropdown-toggle text-dark" href="#" role="button" data-bs-toggle="dropdown" aria-expanded="false"> + Client + </a> + <ul class="dropdown-menu"> + <li><a class="dropdown-item" href="/profile">Profile</a></li> + <li><a class="dropdown-item" href="/">Home</a></li> + </ul> + </li> + </ul> + + <form sec:authorize="isAuthenticated()" method="post" action="/logout"> + <input type="hidden" th:name="${_csrf.parameterName}" + th:value="${_csrf.token}" /> + + <button type="submit" class="btn btn-danger"> + <i class="fa-solid fa-right-from-bracket"></i> + </button> + </form> + + <ul class="navbar-nav" sec:authorize="!isAuthenticated()"> + <li class="nav-item"> + <a href="/register" class="btn btn-outline-primary me-2"> + <i class="fa-regular fa-id-card"></i> + </a> + </li> + <li class="nav-item"> + <a href="/login" class="btn btn-primary"> + <i class="fa-solid fa-right-to-bracket"></i> + </a> + </li> + </ul> + + </div> + </div> +</nav>
\ No newline at end of file diff --git a/src/main/resources/templates/pkgs/add.html b/src/main/resources/templates/pkgs/add.html new file mode 100644 index 0000000..107784f --- /dev/null +++ b/src/main/resources/templates/pkgs/add.html @@ -0,0 +1,109 @@ +<!doctype html> +<html lang="en" xmlns:th="http://www.thymeleaf.org" + xmlns:sec="http://www.thymeleaf.org"> +<head> + <meta charset="utf-8"> + <meta name="viewport" content="width=device-width, initial-scale=1"> + <title>builddb</title> + <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-QWTKZyjpPEjISv5WaRU9OFeRpok6YctnYmDr5pNlyT2bRjXh0JMhjY6hW+ALEwIH" crossorigin="anonymous"> +</head> +<body> + +<nav th:insert="~{/navbar :: navigation}"></nav> + +<hr> + +<div class="container"> + <div class="col-md-8 mx-auto rounded border p-4 m-4"> + <h2 class="text-center mb-5">Add Package</h2> + + <form method="post" enctype="multipart/form-data" th:object="${pkgDto}"> + <input type="hidden" th:name="${_csrf.parameterName}" th:value="${_csrf.token}" /> + + <div class="row mb-3"> + <label class="col-sm-4 col-form-label">Sequence</label> + <div class="col-sm-8"> + <input class="form-control" th:field="${pkgDto.sequence}" /> + <p th:if="${#fields.hasErrors('sequence')}" th:errorclass="text-danger" th:errors="${pkgDto.sequence}"></p> + </div> + </div> + + <div class="row mb-3"> + <label class="col-sm-4 col-form-label">Name</label> + <div class="col-sm-8"> + <input class="form-control" th:field="${pkgDto.name}" /> + <p th:if="${#fields.hasErrors('name')}" th:errorclass="text-danger" th:errors="${pkgDto.name}"></p> + </div> + </div> + + <div class="row mb-3"> + <label class="col-sm-4 col-form-label">Version</label> + <div class="col-sm-8"> + <input class="form-control" th:field="${pkgDto.version}" /> + <p th:if="${#fields.hasErrors('version')}" th:errorclass="text-danger" th:errors="${pkgDto.version}"></p> + </div> + </div> + + <div class="row mb-3"> + <label class="col-sm-4 col-form-label">Configure</label> + <div class="col-sm-8"> + <textarea class="form-control" th:field="${pkgDto.configure}" /> + <p th:if="${#fields.hasErrors('configure')}" th:errorclass="text-danger" th:errors="${pkgDto.configure}"></p> + </div> + </div> + + <div class="row mb-3"> + <label class="col-sm-4 col-form-label">Build</label> + <div class="col-sm-8"> + <textarea class="form-control" th:field="${pkgDto.build}" /> + <p th:if="${#fields.hasErrors('build')}" th:errorclass="text-danger" th:errors="${pkgDto.build}"></p> + </div> + </div> + + <div class="row mb-3"> + <label class="col-sm-4 col-form-label">Install</label> + <div class="col-sm-8"> + <textarea class="form-control" th:field="${pkgDto.install}" /> + <p th:if="${#fields.hasErrors('install')}" th:errorclass="text-danger" th:errors="${pkgDto.install}"></p> + </div> + </div> + + <div class="row mb-3"> + <label class="col-sm-4 col-form-label">Setup</label> + <div class="col-sm-8"> + <textarea class="form-control" th:field="${pkgDto.setup}" /> + <p th:if="${#fields.hasErrors('setup')}" th:errorclass="text-danger" th:errors="${pkgDto.setup}"></p> + </div> + </div> + + <div class="row mb-3"> + <label class="col-sm-4 col-form-label">Notes</label> + <div class="col-sm-8"> + <textarea class="form-control" th:field="${pkgDto.notes}" /> + <p th:if="${#fields.hasErrors('notes')}" th:errorclass="text-danger" th:errors="${pkgDto.notes}"></p> + </div> + </div> + + <div class="row mb-3"> + <label class="col-sm-4 col-form-label">Url</label> + <div class="col-sm-8"> + <textarea class="form-control" th:field="${pkgDto.url}" /> + <p th:if="${#fields.hasErrors('Url')}" th:errorclass="text-danger" th:errors="${pkgDto.url}"></p> + </div> + </div> + + <div class="row"> + <div class="offset-sm-4 col-sm-4 d-grid"> + <button type="submit" class="btn btn-primary">Submit</button> + </div> + <div class="col-sm-4 d-grid"> + <a class="btn btn-outline-primary" href="/pkgs" role="button">Cancel</a> + </div> + </div> + </form> + </div> +</div> + +<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js" integrity="sha384-YvpcrYf0tY3lHB60NNkmXc5s9fDVZLESaAA55NDzOxhy9GkcIdslK1eN7N6jIeHz" crossorigin="anonymous"></script> +</body> +</html>
\ No newline at end of file diff --git a/src/main/resources/templates/pkgs/edit.html b/src/main/resources/templates/pkgs/edit.html new file mode 100644 index 0000000..34ad9a8 --- /dev/null +++ b/src/main/resources/templates/pkgs/edit.html @@ -0,0 +1,109 @@ +<!doctype html> +<html lang="en" xmlns:th="http://www.thymeleaf.org" + xmlns:sec="http://www.thymeleaf.org"> +<head> + <meta charset="utf-8"> + <meta name="viewport" content="width=device-width, initial-scale=1"> + <title>builddb</title> + <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-QWTKZyjpPEjISv5WaRU9OFeRpok6YctnYmDr5pNlyT2bRjXh0JMhjY6hW+ALEwIH" crossorigin="anonymous"> +</head> +<body> + +<nav th:insert="~{/navbar :: navigation}"></nav> + +<hr> + +<div class="container"> + <div class="col-md-8 mx-auto rounded border p-4 m-4"> + <h2 class="text-center mb-5">Edit Package</h2> + + <form method="post" enctype="multipart/form-data" th:object="${pkgDto}"> + <input type="hidden" th:name="${_csrf.parameterName}" th:value="${_csrf.token}" /> + + <div class="row mb-3"> + <label class="col-sm-4 col-form-label">Sequence</label> + <div class="col-sm-8"> + <input class="form-control" th:field="${pkgDto.sequence}" /> + <p th:if="${#fields.hasErrors('sequence')}" th:errorclass="text-danger" th:errors="${pkgDto.sequence}"></p> + </div> + </div> + + <div class="row mb-3"> + <label class="col-sm-4 col-form-label">Name</label> + <div class="col-sm-8"> + <input class="form-control" th:field="${pkgDto.name}" /> + <p th:if="${#fields.hasErrors('name')}" th:errorclass="text-danger" th:errors="${pkgDto.name}"></p> + </div> + </div> + + <div class="row mb-3"> + <label class="col-sm-4 col-form-label">Version</label> + <div class="col-sm-8"> + <input class="form-control" th:field="${pkgDto.version}" /> + <p th:if="${#fields.hasErrors('version')}" th:errorclass="text-danger" th:errors="${pkgDto.version}"></p> + </div> + </div> + + <div class="row mb-3"> + <label class="col-sm-4 col-form-label">Configure</label> + <div class="col-sm-8"> + <textarea class="form-control" th:field="${pkgDto.configure}" /> + <p th:if="${#fields.hasErrors('configure')}" th:errorclass="text-danger" th:errors="${pkgDto.configure}"></p> + </div> + </div> + + <div class="row mb-3"> + <label class="col-sm-4 col-form-label">Build</label> + <div class="col-sm-8"> + <textarea class="form-control" th:field="${pkgDto.build}" /> + <p th:if="${#fields.hasErrors('build')}" th:errorclass="text-danger" th:errors="${pkgDto.build}"></p> + </div> + </div> + + <div class="row mb-3"> + <label class="col-sm-4 col-form-label">Install</label> + <div class="col-sm-8"> + <textarea class="form-control" th:field="${pkgDto.install}" /> + <p th:if="${#fields.hasErrors('install')}" th:errorclass="text-danger" th:errors="${pkgDto.install}"></p> + </div> + </div> + + <div class="row mb-3"> + <label class="col-sm-4 col-form-label">Setup</label> + <div class="col-sm-8"> + <textarea class="form-control" th:field="${pkgDto.setup}" /> + <p th:if="${#fields.hasErrors('setup')}" th:errorclass="text-danger" th:errors="${pkgDto.setup}"></p> + </div> + </div> + + <div class="row mb-3"> + <label class="col-sm-4 col-form-label">Notes</label> + <div class="col-sm-8"> + <textarea class="form-control" th:field="${pkgDto.notes}" /> + <p th:if="${#fields.hasErrors('notes')}" th:errorclass="text-danger" th:errors="${pkgDto.notes}"></p> + </div> + </div> + + <div class="row mb-3"> + <label class="col-sm-4 col-form-label">Url</label> + <div class="col-sm-8"> + <textarea class="form-control" th:field="${pkgDto.url}" /> + <p th:if="${#fields.hasErrors('Url')}" th:errorclass="text-danger" th:errors="${pkgDto.url}"></p> + </div> + </div> + + <div class="row"> + <div class="offset-sm-4 col-sm-4 d-grid"> + <button type="submit" class="btn btn-primary">Submit</button> + </div> + <div class="col-sm-4 d-grid"> + <a class="btn btn-outline-primary" href="/pkgs/" role="button">Cancel</a> + </div> + </div> + </form> + </div> +</div> + +<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js" integrity="sha384-YvpcrYf0tY3lHB60NNkmXc5s9fDVZLESaAA55NDzOxhy9GkcIdslK1eN7N6jIeHz" crossorigin="anonymous"></script> +</body> +</html>
\ No newline at end of file diff --git a/src/main/resources/templates/pkgs/index.html b/src/main/resources/templates/pkgs/index.html new file mode 100644 index 0000000..04eb45a --- /dev/null +++ b/src/main/resources/templates/pkgs/index.html @@ -0,0 +1,62 @@ +<!doctype html> +<html lang="en"> +<head> + <meta charset="utf-8"> + <meta name="viewport" content="width=device-width, initial-scale=1"> + <title>Builddb</title> + <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-QWTKZyjpPEjISv5WaRU9OFeRpok6YctnYmDr5pNlyT2bRjXh0JMhjY6hW+ALEwIH" crossorigin="anonymous"> + <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css"> +</head> +<body> +<nav th:insert="~{/navbar :: navigation}"></nav> + +<hr> +<hr> + +<div class="container"> + <h1 class="text-center my-4">Packages</h1> + <a class="btn btn-primary fa fa-add" href="/pkgs/add"></a> + + <hr> + + <table class="table table-bordered table-hover table-striped"> + <thead> + <tr> + <th id="0" th:replace="~{pkgs/sorting :: sorting('sequence', 'Seq')}">Seq</th> + <th th:replace="~{pkgs/sorting :: sorting('name', 'Name')}">Name</th> + <th>Version</th> + <th>Configure</th> + <th>Build</th> + <th>Install</th> + <th>Setup</th> + <th>Notes</th> + <th>Url</th> + <th>Action</th> + </tr> + </thead> + <tbody class="table-group-divider"> + <tr th:each="pkg : ${pkgs}"> + <td th:id="${pkg.id}" th:text="${pkg.sequence}"></td> + <td th:text="${pkg.name}"></td> + <td th:text="${pkg.version}"></td> + <td th:text="${pkg.configure}"></td> + <td th:text="${pkg.build}"></td> + <td th:text="${pkg.install}"></td> + <td th:text="${pkg.setup}"></td> + <td th:text="${pkg.notes}"></td> + <td th:text="${pkg.url}"></td> + <td style="white-space:nowrap"> + <a class="btn btn-primary btn-sm fa fa-edit" + th:href="@{/pkgs/edit(id=${pkg.id})}"></a> + <a class="btn btn-danger btn-sm fa fa-trash" + th:href="@{/pkgs/delete(id=${pkg.id})}" + onclick="return confirm('Delete?')"></a> + </td> + </tr> + </tbody> + </table> +</div> + +<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js" integrity="sha384-YvpcrYf0tY3lHB60NNkmXc5s9fDVZLESaAA55NDzOxhy9GkcIdslK1eN7N6jIeHz" crossorigin="anonymous"></script> +</body> +</html>
\ No newline at end of file diff --git a/src/main/resources/templates/pkgs/sorting.html b/src/main/resources/templates/pkgs/sorting.html new file mode 100644 index 0000000..199ef68 --- /dev/null +++ b/src/main/resources/templates/pkgs/sorting.html @@ -0,0 +1,6 @@ +<th scope="col" th:fragment="sorting(field, label)"> + <a class="text-decoration-none text-dark" + th:href="@{'/pkgs?' + ${sortField!=null ? '&sort=' + field + ',' + (sortField == field ? reverseSortDirection : sortDirection) : ''}}">[[${label}]]</a> + <span th:if="${sortField == field}" + th:class="${sortDirection == 'asc' ? 'fas fa-arrow-down-short-wide' : 'fas fa-arrow-down-wide-short'}"></span> +</th>
\ No newline at end of file diff --git a/src/main/resources/templates/profile.html b/src/main/resources/templates/profile.html new file mode 100644 index 0000000..aa7fb20 --- /dev/null +++ b/src/main/resources/templates/profile.html @@ -0,0 +1,84 @@ +<!doctype html> +<html lang="en" xmlns:th="http://www.thymeleaf.org"> +<head> + <meta charset="utf-8"> + <meta name="viewport" content="width=device-width, initial-scale=1"> + <title>builddb</title> + <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-QWTKZyjpPEjISv5WaRU9OFeRpok6YctnYmDr5pNlyT2bRjXh0JMhjY6hW+ALEwIH" crossorigin="anonymous"> +</head> +<body> + +<div class="container py-5"> + <div class="rounded border p-4"> + <h2 class="text-center mb-4">Profile</h2> + <hr> + + <div class="row mb-2"> + <div class="col">First Name</div> + <div class="col" th:text="${appUser.firstName}"></div> + </div> + <div class="row mb-2"> + <div class="col">Last Name</div> + <div class="col" th:text="${appUser.lastName}"></div> + </div> + <div class="row mb-2"> + <div class="col">Email</div> + <div class="col" th:text="${appUser.email}"></div> + </div> + <div class="row mb-2"> + <div class="col">Phone</div> + <div class="col" th:text="${appUser.phone}"></div> + </div> + <div class="row mb-2"> + <div class="col">Address</div> + <div class="col" th:text="${appUser.address}"></div> + </div> + <div class="row mb-2"> + <div class="col">Role</div> + <div class="col" th:text="${appUser.role}"></div> + </div> + <div class="row mb-2"> + <div class="col">Created At</div> + <div class="col" th:text="${appUser.createdAt}"></div> + </div> + + <hr> + <a href="/" class="btn btn-link">Home</a> + </div> +</div> + + +<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js" integrity="sha384-YvpcrYf0tY3lHB60NNkmXc5s9fDVZLESaAA55NDzOxhy9GkcIdslK1eN7N6jIeHz" crossorigin="anonymous"></script> +</body> +</html> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/main/resources/templates/register.html b/src/main/resources/templates/register.html new file mode 100644 index 0000000..31f240b --- /dev/null +++ b/src/main/resources/templates/register.html @@ -0,0 +1,155 @@ +<!doctype html> +<html lang="en" xmlns:th="http://www.thymeleaf.org"> +<head> + <meta charset="utf-8"> + <meta name="viewport" content="width=device-width, initial-scale=1"> + <title>builddb</title> + <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-QWTKZyjpPEjISv5WaRU9OFeRpok6YctnYmDr5pNlyT2bRjXh0JMhjY6hW+ALEwIH" crossorigin="anonymous"> +</head> +<body> + +<div class="container py-5"> + <div class="row"> + <div class="col-lg-6 mx-auto rounded border p-4"> + <h2 class="text-center mb-4">Register</h2> + <hr /> + + + + <div th:if="${success}" + class="alert alert-success alert-dismissible fade show" role="alert"> + + <strong>Account Created Successfully!</strong> + <a class="ms-2" href="/login">Login</a> + <button type="button" class="btn-close" data-bs-dismiss="alert" + aria-label="Close"></button> + </div> + + + <form method="post" th:object="${registerDto}"> + <input type="hidden" th:name="${_csrf.parameterName}" + th:value="${_csrf.token}" /> + + + + <div class="row mb-3"> + <label class="col-sm-4 col-form-label">First Name*</label> + <div class="col-sm-8"> + <input class="form-control" th:field="${registerDto.firstName}" > + <p th:if="${#fields.hasErrors('firstName')}" + th:errorclass="text-danger" + th:errors="${registerDto.firstName}" ></p> + </div> + </div> + + <div class="row mb-3"> + <label class="col-sm-4 col-form-label">Last Name*</label> + <div class="col-sm-8"> + <input class="form-control" th:field="${registerDto.lastName}" > + <p th:if="${#fields.hasErrors('lastName')}" + th:errorclass="text-danger" + th:errors="${registerDto.lastName}" ></p> + </div> + </div> + + <div class="row mb-3"> + <label class="col-sm-4 col-form-label">Email*</label> + <div class="col-sm-8"> + <input class="form-control" th:field="${registerDto.email}" > + <p th:if="${#fields.hasErrors('email')}" + th:errorclass="text-danger" + th:errors="${registerDto.email}" ></p> + </div> + </div> + + <div class="row mb-3"> + <label class="col-sm-4 col-form-label">Phone</label> + <div class="col-sm-8"> + <input class="form-control" th:field="${registerDto.phone}" > + <p th:if="${#fields.hasErrors('phone')}" + th:errorclass="text-danger" + th:errors="${registerDto.phone}" ></p> + </div> + </div> + + <div class="row mb-3"> + <label class="col-sm-4 col-form-label">Address</label> + <div class="col-sm-8"> + <input class="form-control" th:field="${registerDto.address}" > + <p th:if="${#fields.hasErrors('address')}" + th:errorclass="text-danger" + th:errors="${registerDto.address}" ></p> + </div> + </div> + + <div class="row mb-3"> + <label class="col-sm-4 col-form-label">Password*</label> + <div class="col-sm-8"> + <input class="form-control" type="password" + th:field="${registerDto.password}" > + <p th:if="${#fields.hasErrors('password')}" + th:errorclass="text-danger" + th:errors="${registerDto.password}" ></p> + </div> + </div> + + <div class="row mb-3"> + <label class="col-sm-4 col-form-label">Confirm Password*</label> + <div class="col-sm-8"> + <input class="form-control" type="password" + th:field="${registerDto.confirmPassword}" > + <p th:if="${#fields.hasErrors('confirmPassword')}" + th:errorclass="text-danger" + th:errors="${registerDto.confirmPassword}" ></p> + </div> + </div> + + <div class="row mb-3"> + <div class="offset-sm-4 col-sm-4 d-grid"> + <button type="submit" class="btn btn-primary">Submit</button> + </div> + <div class="col-sm-4 d-grid"> + <a href="/" class="btn btn-outline-primary">Cancel</a> + </div> + </div> + + </form> + </div> + </div> +</div> + + +<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js" integrity="sha384-YvpcrYf0tY3lHB60NNkmXc5s9fDVZLESaAA55NDzOxhy9GkcIdslK1eN7N6jIeHz" crossorigin="anonymous"></script> +</body> +</html> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/main/resources/templates/user.html b/src/main/resources/templates/user.html new file mode 100644 index 0000000..2791644 --- /dev/null +++ b/src/main/resources/templates/user.html @@ -0,0 +1,53 @@ +<!doctype html> +<html lang="en"> +<head> + <meta charset="utf-8"> + <meta name="viewport" content="width=device-width, initial-scale=1"> + <title>builddb</title> + <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-QWTKZyjpPEjISv5WaRU9OFeRpok6YctnYmDr5pNlyT2bRjXh0JMhjY6hW+ALEwIH" crossorigin="anonymous"> +</head> +<body> + +<div class="container py-5"> + <div class="rounded border p-4"> + <h2 class="text-center mb-4">User Page</h2> + <hr> + <a href="/" class="btn btn-link">Home</a> + </div> +</div> + + +<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js" integrity="sha384-YvpcrYf0tY3lHB60NNkmXc5s9fDVZLESaAA55NDzOxhy9GkcIdslK1eN7N6jIeHz" crossorigin="anonymous"></script> +</body> +</html> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + |