From 3bf20de26ce85616e8e3e0807348c8aee88e67f2 Mon Sep 17 00:00:00 2001 From: LondheShubham153 Date: Sun, 22 Feb 2026 01:10:58 +0530 Subject: [PATCH 01/32] =?UTF-8?q?Modernize=20application=20=E2=80=94=20ful?= =?UTF-8?q?l=20backend=20+=20redesigned=20frontend?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Upgrade Spring Boot 3.4.1, Java 21, modern MySQL connector - Add Spring Security with BCrypt, form login, CSRF - Add JPA entities (Account, Transaction), repositories, service layer - Add controller with all endpoints (login, register, dashboard, deposit, withdraw, transfer, transactions) - Redesign all templates with Bootstrap 5, glassmorphism, dark/light theme - Add actuator + Prometheus metrics, virtual threads - Externalize DB config via environment variables --- mvnw | 0 pom.xml | 21 +- .../bankapp/config/SecurityConfig.java | 40 ++ .../bankapp/controller/BankController.java | 86 +++ .../com/example/bankapp/model/Account.java | 112 ++++ .../example/bankapp/model/Transaction.java | 78 +++ .../bankapp/repository/AccountRepository.java | 10 + .../repository/TransactionRepository.java | 10 + .../bankapp/service/AccountService.java | 98 ++++ src/main/resources/application.properties | 19 +- src/main/resources/static/css/bankapp.css | 507 ++++++++++++++++++ src/main/resources/static/js/theme.js | 33 ++ src/main/resources/templates/dashboard.html | 241 +++------ .../resources/templates/fragments/layout.html | 62 +++ src/main/resources/templates/login.html | 212 +++----- src/main/resources/templates/register.html | 235 +++----- .../resources/templates/transactions.html | 177 ++---- 17 files changed, 1337 insertions(+), 604 deletions(-) mode change 100644 => 100755 mvnw create mode 100644 src/main/java/com/example/bankapp/config/SecurityConfig.java create mode 100644 src/main/java/com/example/bankapp/controller/BankController.java create mode 100644 src/main/java/com/example/bankapp/model/Account.java create mode 100644 src/main/java/com/example/bankapp/model/Transaction.java create mode 100644 src/main/java/com/example/bankapp/repository/AccountRepository.java create mode 100644 src/main/java/com/example/bankapp/repository/TransactionRepository.java create mode 100644 src/main/java/com/example/bankapp/service/AccountService.java create mode 100644 src/main/resources/static/css/bankapp.css create mode 100644 src/main/resources/static/js/theme.js create mode 100644 src/main/resources/templates/fragments/layout.html diff --git a/mvnw b/mvnw old mode 100644 new mode 100755 diff --git a/pom.xml b/pom.xml index 38bd6d74..c9942e6c 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ org.springframework.boot spring-boot-starter-parent - 3.3.3 + 3.4.1 com.example @@ -27,7 +27,7 @@ - 17 + 21 @@ -46,15 +46,26 @@ org.springframework.boot spring-boot-starter-web + + org.springframework.boot + spring-boot-starter-actuator + + + org.springframework.boot + spring-boot-starter-validation + org.thymeleaf.extras thymeleaf-extras-springsecurity6 + + io.micrometer + micrometer-registry-prometheus + - mysql - mysql-connector-java - 8.0.33 + com.mysql + mysql-connector-j runtime diff --git a/src/main/java/com/example/bankapp/config/SecurityConfig.java b/src/main/java/com/example/bankapp/config/SecurityConfig.java new file mode 100644 index 00000000..f7c0a8cf --- /dev/null +++ b/src/main/java/com/example/bankapp/config/SecurityConfig.java @@ -0,0 +1,40 @@ +package com.example.bankapp.config; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +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 +public class SecurityConfig { + + @Bean + public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { + http + .authorizeHttpRequests(auth -> auth + .requestMatchers("/register", "/login", "/css/**", "/js/**", "/actuator/**").permitAll() + .anyRequest().authenticated() + ) + .formLogin(form -> form + .loginPage("/login") + .defaultSuccessUrl("/dashboard", true) + .permitAll() + ) + .logout(logout -> logout + .logoutUrl("/logout") + .logoutSuccessUrl("/login?logout") + .permitAll() + ); + + return http.build(); + } + + @Bean + public PasswordEncoder passwordEncoder() { + return new BCryptPasswordEncoder(); + } +} diff --git a/src/main/java/com/example/bankapp/controller/BankController.java b/src/main/java/com/example/bankapp/controller/BankController.java new file mode 100644 index 00000000..94ee8923 --- /dev/null +++ b/src/main/java/com/example/bankapp/controller/BankController.java @@ -0,0 +1,86 @@ +package com.example.bankapp.controller; + +import com.example.bankapp.model.Account; +import com.example.bankapp.service.AccountService; +import org.springframework.security.core.annotation.AuthenticationPrincipal; +import org.springframework.stereotype.Controller; +import org.springframework.ui.Model; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.servlet.mvc.support.RedirectAttributes; + +import java.math.BigDecimal; + +@Controller +public class BankController { + + private final AccountService accountService; + + public BankController(AccountService accountService) { + this.accountService = accountService; + } + + @GetMapping("/login") + public String loginPage() { + return "login"; + } + + @GetMapping("/register") + public String registerPage() { + return "register"; + } + + @PostMapping("/register") + public String register(@RequestParam String username, + @RequestParam String password, + Model model) { + if (accountService.registerAccount(username, password)) { + return "redirect:/login?registered"; + } + model.addAttribute("error", true); + return "register"; + } + + @GetMapping("/dashboard") + public String dashboard(@AuthenticationPrincipal Account account, Model model) { + model.addAttribute("account", account); + return "dashboard"; + } + + @PostMapping("/deposit") + public String deposit(@AuthenticationPrincipal Account account, + @RequestParam BigDecimal amount, + RedirectAttributes redirectAttributes) { + accountService.deposit(account, amount); + return "redirect:/dashboard"; + } + + @PostMapping("/withdraw") + public String withdraw(@AuthenticationPrincipal Account account, + @RequestParam BigDecimal amount, + RedirectAttributes redirectAttributes) { + if (!accountService.withdraw(account, amount)) { + redirectAttributes.addFlashAttribute("error", "Insufficient funds."); + } + return "redirect:/dashboard"; + } + + @PostMapping("/transfer") + public String transfer(@AuthenticationPrincipal Account account, + @RequestParam String toUsername, + @RequestParam BigDecimal amount, + RedirectAttributes redirectAttributes) { + String error = accountService.transferAmount(account, toUsername, amount); + if (error != null) { + redirectAttributes.addFlashAttribute("error", error); + } + return "redirect:/dashboard"; + } + + @GetMapping("/transactions") + public String transactions(@AuthenticationPrincipal Account account, Model model) { + model.addAttribute("transactions", accountService.getTransactionHistory(account)); + return "transactions"; + } +} diff --git a/src/main/java/com/example/bankapp/model/Account.java b/src/main/java/com/example/bankapp/model/Account.java new file mode 100644 index 00000000..a85b5c5c --- /dev/null +++ b/src/main/java/com/example/bankapp/model/Account.java @@ -0,0 +1,112 @@ +package com.example.bankapp.model; + +import jakarta.persistence.*; +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.authority.SimpleGrantedAuthority; +import org.springframework.security.core.userdetails.UserDetails; + +import java.math.BigDecimal; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +@Entity +@Table(name = "accounts") +public class Account implements UserDetails { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @Column(unique = true, nullable = false) + private String username; + + @Column(nullable = false) + private String password; + + @Column(nullable = false, precision = 19, scale = 2) + private BigDecimal balance = BigDecimal.ZERO; + + @OneToMany(mappedBy = "account", cascade = CascadeType.ALL, fetch = FetchType.LAZY) + private List transactions = new ArrayList<>(); + + public Account() { + } + + public Account(String username, String password) { + this.username = username; + this.password = password; + this.balance = BigDecimal.ZERO; + } + + // Getters and setters + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + @Override + public String getUsername() { + return username; + } + + public void setUsername(String username) { + this.username = username; + } + + @Override + public String getPassword() { + return password; + } + + public void setPassword(String password) { + this.password = password; + } + + public BigDecimal getBalance() { + return balance; + } + + public void setBalance(BigDecimal balance) { + this.balance = balance; + } + + public List getTransactions() { + return transactions; + } + + public void setTransactions(List transactions) { + this.transactions = transactions; + } + + // UserDetails implementation + + @Override + public Collection getAuthorities() { + return List.of(new SimpleGrantedAuthority("ROLE_USER")); + } + + @Override + public boolean isAccountNonExpired() { + return true; + } + + @Override + public boolean isAccountNonLocked() { + return true; + } + + @Override + public boolean isCredentialsNonExpired() { + return true; + } + + @Override + public boolean isEnabled() { + return true; + } +} diff --git a/src/main/java/com/example/bankapp/model/Transaction.java b/src/main/java/com/example/bankapp/model/Transaction.java new file mode 100644 index 00000000..799de060 --- /dev/null +++ b/src/main/java/com/example/bankapp/model/Transaction.java @@ -0,0 +1,78 @@ +package com.example.bankapp.model; + +import jakarta.persistence.*; + +import java.math.BigDecimal; +import java.time.LocalDateTime; + +@Entity +@Table(name = "transactions") +public class Transaction { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @Column(nullable = false, precision = 19, scale = 2) + private BigDecimal amount; + + @Column(nullable = false) + private String type; + + @Column(nullable = false) + private LocalDateTime timestamp; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "account_id") + private Account account; + + public Transaction() { + } + + public Transaction(BigDecimal amount, String type, LocalDateTime timestamp, Account account) { + this.amount = amount; + this.type = type; + this.timestamp = timestamp; + this.account = account; + } + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public BigDecimal getAmount() { + return amount; + } + + public void setAmount(BigDecimal amount) { + this.amount = amount; + } + + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; + } + + public LocalDateTime getTimestamp() { + return timestamp; + } + + public void setTimestamp(LocalDateTime timestamp) { + this.timestamp = timestamp; + } + + public Account getAccount() { + return account; + } + + public void setAccount(Account account) { + this.account = account; + } +} diff --git a/src/main/java/com/example/bankapp/repository/AccountRepository.java b/src/main/java/com/example/bankapp/repository/AccountRepository.java new file mode 100644 index 00000000..72553370 --- /dev/null +++ b/src/main/java/com/example/bankapp/repository/AccountRepository.java @@ -0,0 +1,10 @@ +package com.example.bankapp.repository; + +import com.example.bankapp.model.Account; +import org.springframework.data.jpa.repository.JpaRepository; + +import java.util.Optional; + +public interface AccountRepository extends JpaRepository { + Optional findByUsername(String username); +} diff --git a/src/main/java/com/example/bankapp/repository/TransactionRepository.java b/src/main/java/com/example/bankapp/repository/TransactionRepository.java new file mode 100644 index 00000000..89408151 --- /dev/null +++ b/src/main/java/com/example/bankapp/repository/TransactionRepository.java @@ -0,0 +1,10 @@ +package com.example.bankapp.repository; + +import com.example.bankapp.model.Transaction; +import org.springframework.data.jpa.repository.JpaRepository; + +import java.util.List; + +public interface TransactionRepository extends JpaRepository { + List findByAccountIdOrderByTimestampDesc(Long accountId); +} diff --git a/src/main/java/com/example/bankapp/service/AccountService.java b/src/main/java/com/example/bankapp/service/AccountService.java new file mode 100644 index 00000000..5fbffbf9 --- /dev/null +++ b/src/main/java/com/example/bankapp/service/AccountService.java @@ -0,0 +1,98 @@ +package com.example.bankapp.service; + +import com.example.bankapp.model.Account; +import com.example.bankapp.model.Transaction; +import com.example.bankapp.repository.AccountRepository; +import com.example.bankapp.repository.TransactionRepository; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.security.core.userdetails.UserDetailsService; +import org.springframework.security.core.userdetails.UsernameNotFoundException; +import org.springframework.security.crypto.password.PasswordEncoder; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.math.BigDecimal; +import java.time.LocalDateTime; +import java.util.List; + +@Service +public class AccountService implements UserDetailsService { + + private final AccountRepository accountRepository; + private final TransactionRepository transactionRepository; + private final PasswordEncoder passwordEncoder; + + public AccountService(AccountRepository accountRepository, + TransactionRepository transactionRepository, + PasswordEncoder passwordEncoder) { + this.accountRepository = accountRepository; + this.transactionRepository = transactionRepository; + this.passwordEncoder = passwordEncoder; + } + + @Override + public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { + return accountRepository.findByUsername(username) + .orElseThrow(() -> new UsernameNotFoundException("User not found: " + username)); + } + + public boolean registerAccount(String username, String password) { + if (accountRepository.findByUsername(username).isPresent()) { + return false; + } + Account account = new Account(username, passwordEncoder.encode(password)); + accountRepository.save(account); + return true; + } + + @Transactional + public void deposit(Account account, BigDecimal amount) { + account.setBalance(account.getBalance().add(amount)); + accountRepository.save(account); + + Transaction transaction = new Transaction(amount, "Deposit", LocalDateTime.now(), account); + transactionRepository.save(transaction); + } + + @Transactional + public boolean withdraw(Account account, BigDecimal amount) { + if (account.getBalance().compareTo(amount) < 0) { + return false; + } + account.setBalance(account.getBalance().subtract(amount)); + accountRepository.save(account); + + Transaction transaction = new Transaction(amount, "Withdrawal", LocalDateTime.now(), account); + transactionRepository.save(transaction); + return true; + } + + @Transactional + public String transferAmount(Account from, String toUsername, BigDecimal amount) { + if (from.getUsername().equals(toUsername)) { + return "Cannot transfer to yourself."; + } + if (from.getBalance().compareTo(amount) < 0) { + return "Insufficient funds."; + } + Account to = accountRepository.findByUsername(toUsername).orElse(null); + if (to == null) { + return "Recipient not found."; + } + + from.setBalance(from.getBalance().subtract(amount)); + to.setBalance(to.getBalance().add(amount)); + accountRepository.save(from); + accountRepository.save(to); + + LocalDateTime now = LocalDateTime.now(); + transactionRepository.save(new Transaction(amount, "Transfer Out", now, from)); + transactionRepository.save(new Transaction(amount, "Transfer In", now, to)); + + return null; // null means success + } + + public List getTransactionHistory(Account account) { + return transactionRepository.findByAccountIdOrderByTimestampDesc(account.getId()); + } +} diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index 08663a63..f934de60 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -1,11 +1,22 @@ spring.application.name=bankapp + # MySQL Database configuration -spring.datasource.url=jdbc:mysql://localhost:3306/bankappdb?useSSL=false&serverTimezone=UTC -spring.datasource.username=root -spring.datasource.password=Test@123 +spring.datasource.url=jdbc:mysql://${MYSQL_HOST:localhost}:${MYSQL_PORT:3306}/${MYSQL_DATABASE:bankappdb}?useSSL=false&allowPublicKeyRetrieval=true +spring.datasource.username=${MYSQL_USER:root} +spring.datasource.password=${MYSQL_PASSWORD:Test@123} spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver # JPA & Hibernate configuration spring.jpa.hibernate.ddl-auto=update -spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL8Dialect spring.jpa.show-sql=true +spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQLDialect + +# Virtual threads (Java 21) +spring.threads.virtual.enabled=true + +# Actuator +management.endpoints.web.exposure.include=health,info,metrics,prometheus +management.endpoint.health.show-details=when-authorized + +# Structured logging +logging.pattern.console=%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n diff --git a/src/main/resources/static/css/bankapp.css b/src/main/resources/static/css/bankapp.css new file mode 100644 index 00000000..34a9046d --- /dev/null +++ b/src/main/resources/static/css/bankapp.css @@ -0,0 +1,507 @@ +/* ===== CSS Custom Properties ===== */ +:root { + --bg-primary: #0a0a0a; + --bg-surface: rgba(255, 255, 255, 0.03); + --text-primary: #f1f5f9; + --text-muted: #94a3b8; + --accent: #3b82f6; + --accent-hover: #2563eb; + --accent-glow: rgba(59, 130, 246, 0.4); + --success: #22c55e; + --danger: #ef4444; + --border-color: rgba(255, 255, 255, 0.08); + --border-radius: 16px; + --border-radius-sm: 10px; + --transition-speed: 0.3s; + --glass-bg: rgba(255, 255, 255, 0.05); + --glass-border: rgba(255, 255, 255, 0.1); + --glass-shadow: 0 8px 32px rgba(0, 0, 0, 0.4); + --gradient-bg: linear-gradient(135deg, #0a0a0a 0%, #1a1a2e 50%, #16213e 100%); + --font-stack: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif; +} + +[data-theme="light"] { + --bg-primary: #f8fafc; + --bg-surface: rgba(0, 0, 0, 0.02); + --text-primary: #0f172a; + --text-muted: #64748b; + --border-color: rgba(0, 0, 0, 0.08); + --glass-bg: rgba(255, 255, 255, 0.7); + --glass-border: rgba(0, 0, 0, 0.08); + --glass-shadow: 0 8px 32px rgba(0, 0, 0, 0.1); + --gradient-bg: linear-gradient(135deg, #f8fafc 0%, #e2e8f0 50%, #dbeafe 100%); +} + +/* ===== Reset & Base ===== */ +*, *::before, *::after { + box-sizing: border-box; +} + +body { + font-family: var(--font-stack); + background: var(--gradient-bg); + color: var(--text-primary); + margin: 0; + padding: 0; + min-height: 100vh; + transition: background var(--transition-speed), color var(--transition-speed); +} + +a { + color: var(--accent); + text-decoration: none; + transition: color var(--transition-speed); +} + +a:hover { + color: var(--accent-hover); +} + +/* ===== Navbar ===== */ +.navbar-app { + background: var(--glass-bg); + backdrop-filter: blur(12px); + -webkit-backdrop-filter: blur(12px); + border-bottom: 1px solid var(--glass-border); + padding: 0.75rem 1.5rem; + position: sticky; + top: 0; + z-index: 1000; +} + +.navbar-app .navbar-brand { + font-weight: 700; + font-size: 1.25rem; + color: var(--text-primary) !important; + display: flex; + align-items: center; + gap: 0.5rem; +} + +.navbar-app .nav-link { + color: var(--text-muted) !important; + font-weight: 500; + padding: 0.5rem 1rem !important; + border-radius: var(--border-radius-sm); + transition: all var(--transition-speed); +} + +.navbar-app .nav-link:hover, +.navbar-app .nav-link.active { + color: var(--text-primary) !important; + background: var(--bg-surface); +} + +.theme-toggle { + background: var(--glass-bg); + border: 1px solid var(--glass-border); + color: var(--text-primary); + border-radius: 50%; + width: 40px; + height: 40px; + display: flex; + align-items: center; + justify-content: center; + cursor: pointer; + transition: all var(--transition-speed); + font-size: 1.1rem; +} + +.theme-toggle:hover { + background: var(--accent); + color: #fff; + border-color: var(--accent); +} + +/* ===== Glass Card ===== */ +.glass-card { + background: var(--glass-bg); + backdrop-filter: blur(12px); + -webkit-backdrop-filter: blur(12px); + border: 1px solid var(--glass-border); + border-radius: var(--border-radius); + box-shadow: var(--glass-shadow); + padding: 2rem; + transition: transform var(--transition-speed), box-shadow var(--transition-speed); +} + +.glass-card:hover { + transform: translateY(-2px); + box-shadow: 0 12px 40px rgba(0, 0, 0, 0.5); +} + +[data-theme="light"] .glass-card:hover { + box-shadow: 0 12px 40px rgba(0, 0, 0, 0.15); +} + +/* ===== Auth Pages (Login/Register) ===== */ +.auth-wrapper { + min-height: 100vh; + display: flex; + flex-direction: column; +} + +.auth-container { + flex: 1; + display: flex; + align-items: center; + justify-content: center; + padding: 2rem 1rem; +} + +.auth-card { + width: 100%; + max-width: 440px; + animation: fadeInUp 0.5s ease-out; +} + +.auth-card h2 { + font-weight: 700; + margin-bottom: 0.5rem; +} + +.auth-card .text-muted { + color: var(--text-muted) !important; +} + +/* ===== Form Styles ===== */ +.form-floating > .form-control { + background: var(--bg-surface); + border: 1px solid var(--border-color); + color: var(--text-primary); + border-radius: var(--border-radius-sm); + transition: border-color var(--transition-speed), box-shadow var(--transition-speed); +} + +.form-floating > .form-control:focus { + background: var(--bg-surface); + border-color: var(--accent); + box-shadow: 0 0 0 3px var(--accent-glow); + color: var(--text-primary); +} + +.form-floating > label { + color: var(--text-muted); +} + +.form-floating > .form-control:focus ~ label, +.form-floating > .form-control:not(:placeholder-shown) ~ label { + color: var(--accent); +} + +/* ===== Buttons ===== */ +.btn-accent { + background: var(--accent); + color: #fff; + border: none; + border-radius: var(--border-radius-sm); + padding: 0.75rem 1.5rem; + font-weight: 600; + transition: all var(--transition-speed); +} + +.btn-accent:hover { + background: var(--accent-hover); + color: #fff; + transform: translateY(-1px); + box-shadow: 0 4px 12px var(--accent-glow); +} + +.btn-glass { + background: var(--glass-bg); + color: var(--text-primary); + border: 1px solid var(--glass-border); + border-radius: var(--border-radius-sm); + padding: 0.75rem 1.5rem; + font-weight: 500; + transition: all var(--transition-speed); +} + +.btn-glass:hover { + background: var(--accent); + color: #fff; + border-color: var(--accent); +} + +/* ===== Password Toggle ===== */ +.input-group-password { + position: relative; +} + +.input-group-password .form-control { + padding-right: 3rem; +} + +.password-toggle { + position: absolute; + right: 12px; + top: 50%; + transform: translateY(-50%); + background: none; + border: none; + color: var(--text-muted); + cursor: pointer; + z-index: 5; + padding: 0.25rem; +} + +.password-toggle:hover { + color: var(--accent); +} + +/* ===== Dashboard ===== */ +.dashboard-wrapper { + padding: 2rem 1rem; + max-width: 1100px; + margin: 0 auto; +} + +.balance-card { + text-align: center; + padding: 2.5rem 2rem; + margin-bottom: 2rem; +} + +.balance-label { + font-size: 0.875rem; + text-transform: uppercase; + letter-spacing: 0.1em; + color: var(--text-muted); + margin-bottom: 0.5rem; +} + +.balance-amount { + font-size: 3rem; + font-weight: 800; + color: var(--text-primary); + line-height: 1; +} + +.balance-amount .currency { + color: var(--accent); +} + +.account-info { + display: flex; + justify-content: center; + gap: 2rem; + margin-top: 1rem; + color: var(--text-muted); + font-size: 0.875rem; +} + +.action-cards { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); + gap: 1.5rem; + margin-bottom: 2rem; +} + +.action-card { + padding: 1.5rem; +} + +.action-card h5 { + font-weight: 600; + margin-bottom: 1rem; + display: flex; + align-items: center; + gap: 0.5rem; +} + +.action-card .form-control { + background: var(--bg-surface); + border: 1px solid var(--border-color); + color: var(--text-primary); + border-radius: var(--border-radius-sm); + padding: 0.75rem 1rem; +} + +.action-card .form-control:focus { + border-color: var(--accent); + box-shadow: 0 0 0 3px var(--accent-glow); +} + +.action-card .form-label { + color: var(--text-muted); + font-size: 0.875rem; + font-weight: 500; +} + +/* ===== Transactions Table ===== */ +.transactions-wrapper { + padding: 2rem 1rem; + max-width: 900px; + margin: 0 auto; +} + +.table-glass { + width: 100%; + border-collapse: separate; + border-spacing: 0; +} + +.table-glass thead th { + background: var(--bg-surface); + color: var(--text-muted); + font-weight: 600; + font-size: 0.75rem; + text-transform: uppercase; + letter-spacing: 0.05em; + padding: 1rem; + border-bottom: 1px solid var(--border-color); +} + +.table-glass tbody td { + padding: 1rem; + border-bottom: 1px solid var(--border-color); + color: var(--text-primary); + vertical-align: middle; +} + +.table-glass tbody tr { + transition: background var(--transition-speed); +} + +.table-glass tbody tr:hover { + background: var(--bg-surface); +} + +.amount-positive { + color: var(--success); + font-weight: 600; +} + +.amount-negative { + color: var(--danger); + font-weight: 600; +} + +.badge-type { + display: inline-block; + padding: 0.25rem 0.75rem; + border-radius: 999px; + font-size: 0.75rem; + font-weight: 600; +} + +.badge-deposit { + background: rgba(34, 197, 94, 0.15); + color: var(--success); +} + +.badge-withdrawal { + background: rgba(239, 68, 68, 0.15); + color: var(--danger); +} + +.badge-transfer-out { + background: rgba(239, 68, 68, 0.15); + color: var(--danger); +} + +.badge-transfer-in { + background: rgba(34, 197, 94, 0.15); + color: var(--success); +} + +.empty-state { + text-align: center; + padding: 4rem 2rem; + color: var(--text-muted); +} + +.empty-state i { + font-size: 3rem; + margin-bottom: 1rem; + opacity: 0.5; +} + +/* ===== Alert Styles ===== */ +.alert-glass { + background: rgba(239, 68, 68, 0.1); + border: 1px solid rgba(239, 68, 68, 0.3); + color: var(--danger); + border-radius: var(--border-radius-sm); + padding: 0.75rem 1rem; +} + +.alert-glass-success { + background: rgba(34, 197, 94, 0.1); + border: 1px solid rgba(34, 197, 94, 0.3); + color: var(--success); +} + +/* ===== Footer ===== */ +.footer-app { + background: var(--glass-bg); + backdrop-filter: blur(12px); + -webkit-backdrop-filter: blur(12px); + border-top: 1px solid var(--glass-border); + padding: 1.25rem; + text-align: center; + color: var(--text-muted); + font-size: 0.85rem; + margin-top: auto; +} + +.footer-app a { + color: var(--accent); + font-weight: 500; +} + +/* ===== Animations ===== */ +@keyframes fadeInUp { + from { + opacity: 0; + transform: translateY(20px); + } + to { + opacity: 1; + transform: translateY(0); + } +} + +.fade-in { + animation: fadeInUp 0.5s ease-out; +} + +.fade-in-delay-1 { animation-delay: 0.1s; animation-fill-mode: both; } +.fade-in-delay-2 { animation-delay: 0.2s; animation-fill-mode: both; } +.fade-in-delay-3 { animation-delay: 0.3s; animation-fill-mode: both; } + +/* ===== Responsive ===== */ +@media (max-width: 768px) { + .balance-amount { + font-size: 2.25rem; + } + + .account-info { + flex-direction: column; + gap: 0.5rem; + } + + .action-cards { + grid-template-columns: 1fr; + } + + .glass-card { + padding: 1.5rem; + } + + .table-glass thead th, + .table-glass tbody td { + padding: 0.75rem 0.5rem; + font-size: 0.875rem; + } +} + +@media (max-width: 480px) { + .balance-amount { + font-size: 1.75rem; + } + + .auth-card { + padding: 1.5rem; + } +} diff --git a/src/main/resources/static/js/theme.js b/src/main/resources/static/js/theme.js new file mode 100644 index 00000000..e27c1f74 --- /dev/null +++ b/src/main/resources/static/js/theme.js @@ -0,0 +1,33 @@ +(function () { + const STORAGE_KEY = 'bankapp-theme'; + + function getPreferred() { + const stored = localStorage.getItem(STORAGE_KEY); + if (stored) return stored; + return window.matchMedia('(prefers-color-scheme: light)').matches ? 'light' : 'dark'; + } + + function apply(theme) { + document.documentElement.setAttribute('data-theme', theme); + localStorage.setItem(STORAGE_KEY, theme); + const icon = document.getElementById('themeIcon'); + if (icon) { + icon.className = theme === 'dark' ? 'bi bi-sun-fill' : 'bi bi-moon-fill'; + } + } + + // Apply immediately to prevent flash + apply(getPreferred()); + + document.addEventListener('DOMContentLoaded', function () { + const btn = document.getElementById('themeToggle'); + if (btn) { + btn.addEventListener('click', function () { + const current = document.documentElement.getAttribute('data-theme') || 'dark'; + apply(current === 'dark' ? 'light' : 'dark'); + }); + } + // Re-apply to update icon after DOM is ready + apply(getPreferred()); + }); +})(); diff --git a/src/main/resources/templates/dashboard.html b/src/main/resources/templates/dashboard.html index 24eb6990..1d84ad6a 100644 --- a/src/main/resources/templates/dashboard.html +++ b/src/main/resources/templates/dashboard.html @@ -1,201 +1,86 @@ - - - Dashboard - Goldencat Bank - - - + + - - -
-
-

-

-
+
+ + +
+ +
+
Total Balance
+
+ $0.00 +
+ +
- - + +
+ +
-
- -
- -
-
-
- - + +
+ +
+
Deposit
+ +
+ +
- +
-
- -
- -
-
-
- - + +
+
Withdraw
+ +
+ +
- +
-
- -
- -
-
-
- - + +
+
Transfer
+ +
+ +
-
- - +
+ +
- +
-

+
- - - + diff --git a/src/main/resources/templates/fragments/layout.html b/src/main/resources/templates/fragments/layout.html new file mode 100644 index 00000000..07185ea6 --- /dev/null +++ b/src/main/resources/templates/fragments/layout.html @@ -0,0 +1,62 @@ + + + + + + + + BankApp + + + + + + + + + + + + + +
+

© 2026 BankApp. Built with Spring Boot.

+
+ + diff --git a/src/main/resources/templates/login.html b/src/main/resources/templates/login.html index 57fd3666..b160bda6 100644 --- a/src/main/resources/templates/login.html +++ b/src/main/resources/templates/login.html @@ -1,156 +1,72 @@ - - - Login - Goldencat Bank - - - - + + - - -
-

Login

-
-
- - +
+ + +
+
+
+

Welcome back

+

Sign in to your account

+
+ +
+ Invalid username or password. +
+ +
+ You have been logged out. +
+ +
+ Registration successful. Please sign in. +
+ + +
+ + +
+ +
+ + + +
+ + + + +

+ Don't have an account? Create one +

-
- - -
- - -

Don't have an account? Register here

- -
- Invalid username or password.
-
- - +
+
+ + diff --git a/src/main/resources/templates/register.html b/src/main/resources/templates/register.html index 4fbeb045..a54ca3a3 100644 --- a/src/main/resources/templates/register.html +++ b/src/main/resources/templates/register.html @@ -1,156 +1,95 @@ - - - Register - Goldencat Bank - - - - + + - - -
-

Register a New Account

-
-
- - +
+ + +
+
+
+

Create Account

+

Join BankApp today

+
+ +
+ Username already taken. Please choose another. +
+ + +
+ + +
+ +
+ + + +
+ +
+
+
+
+ +
+ + + + +

+ Already have an account? Sign in +

-
- - -
- - -

Already have an account? Login here

- -
- User already present.
-
- - +
+
+ + diff --git a/src/main/resources/templates/transactions.html b/src/main/resources/templates/transactions.html index 892042c8..e0e48bbe 100644 --- a/src/main/resources/templates/transactions.html +++ b/src/main/resources/templates/transactions.html @@ -1,129 +1,64 @@ - - - Transaction History - Goldencat Bank - - - + + - - -
-

Transaction History

-
- - - - - - - - - - - - - - - - - -
IDTypeAmountDate
- -
+
+ + +
+
+

Transaction History

+ + Dashboard + +
+ +
+ +
+
+ + + + + + + + + + + + + + + + + +
IDTypeAmountDate
+ + +
+
+
+ + +
+ +
No transactions yet
+

Your transaction history will appear here once you make your first deposit.

+ + Make a Deposit + +
+
-

Back to Dashboard

+
+ From a0602102cff78775df2336ee786bb7032e740f49 Mon Sep 17 00:00:00 2001 From: LondheShubham153 Date: Sun, 22 Feb 2026 01:14:40 +0530 Subject: [PATCH 02/32] Add README and ROADMAP for DevOps progression --- README.md | 71 +++++++++++++++++++++++++++++++++++++++++++ ROADMAP.md | 88 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 159 insertions(+) create mode 100644 README.md create mode 100644 ROADMAP.md diff --git a/README.md b/README.md new file mode 100644 index 00000000..fa4154b3 --- /dev/null +++ b/README.md @@ -0,0 +1,71 @@ +# AI-BankApp-DevOps + +A Spring Boot banking application used as a base for learning end-to-end DevOps — from Docker to Kubernetes to GitOps. + +## Tech Stack + +- **Backend:** Spring Boot 3.4.1, Java 21, Spring Security, JPA/Hibernate +- **Frontend:** Thymeleaf, Bootstrap 5, Glassmorphism UI with dark/light theme +- **Database:** MySQL 8.0 +- **AI:** Ollama (self-hosted LLM chatbot, zero cost) +- **DevOps:** Docker, GitHub Actions, Kubernetes, Helm, Terraform, Prometheus, Grafana, ArgoCD + +## Branches + +| Branch | Description | +|--------|-------------| +| `start` | Modernized app — full backend + frontend (developer handoff) | +| `docker` | Adds Dockerfile, multi-stage build, docker-compose | +| `ai` | Adds AI chatbot powered by Ollama | +| `main` | End-to-end DevOps (WIP) | + +See [ROADMAP.md](ROADMAP.md) for the full progression. + +## Quick Start + +### Run locally (needs Java 21 + MySQL) + +```bash +# Create database +mysql -u root -p -e "CREATE DATABASE bankappdb;" + +# Run the app +./mvnw spring-boot:run +``` + +### Run with Docker (recommended) + +```bash +# Switch to docker branch +git checkout docker + +# Start everything +docker compose up -d --build + +# Visit http://localhost:8080 +``` + +### Run with AI Chatbot + +```bash +# Switch to ai branch +git checkout ai + +# Start everything (includes Ollama) +docker compose up -d --build + +# Pull the AI model (one-time) +docker exec bankapp-ollama ollama pull tinyllama + +# Visit http://localhost:8080 +``` + +## Features + +- User registration & login with BCrypt passwords +- Deposit, withdraw, transfer between accounts +- Transaction history with color-coded entries +- Dark/light theme toggle (persists across sessions) +- AI chatbot that knows your balance and recent transactions +- Prometheus metrics at `/actuator/prometheus` +- Health check at `/actuator/health` diff --git a/ROADMAP.md b/ROADMAP.md new file mode 100644 index 00000000..c4165a95 --- /dev/null +++ b/ROADMAP.md @@ -0,0 +1,88 @@ +# DevOps Roadmap — BankApp + +A step-by-step progression from code to production-grade DevOps. +Each phase builds on the previous one. Check off as you go. + +--- + +## Phase 1: Application (`start` branch) +- [x] Spring Boot backend with MySQL +- [x] Thymeleaf frontend with modern UI +- [x] Spring Security (login, register, CSRF) +- [x] Actuator + Prometheus metrics endpoint +- [x] Externalized config via environment variables + +## Phase 2: Docker (`docker` branch) +- [x] Dockerfile (simple) +- [x] Dockerfile.multistage (optimized image) +- [x] docker-compose.yml (app + MySQL) +- [ ] .dockerignore file +- [ ] Push image to Docker Hub + +## Phase 3: CI/CD (`cicd` branch) +- [ ] GitHub Actions workflow — build & test on PR +- [ ] Build Docker image in CI +- [ ] Push image to Docker Hub from CI +- [ ] Tag images with git SHA + `latest` + +## Phase 4: Kubernetes (`k8s` branch) +- [ ] Deployment manifest (app) +- [ ] Service manifest (ClusterIP) +- [ ] ConfigMap (app config) +- [ ] Secret (DB credentials) +- [ ] MySQL StatefulSet or external DB +- [ ] Ingress with host-based routing +- [ ] Deploy to a local cluster (minikube / kind) + +## Phase 5: Helm (`helm` branch) +- [ ] Helm chart for BankApp +- [ ] values.yaml for dev / prod +- [ ] Install via `helm install` + +## Phase 6: IaC with Terraform (`terraform` branch) +- [ ] Provision AWS EKS cluster (or equivalent) +- [ ] RDS MySQL instance +- [ ] VPC, subnets, security groups +- [ ] State stored in S3 + DynamoDB lock + +## Phase 7: Monitoring (`monitoring` branch) +- [ ] Prometheus scraping `/actuator/prometheus` +- [ ] Grafana dashboard for app metrics +- [ ] Alerting rules (high error rate, pod restarts) + +## Phase 8: GitOps (`gitops` branch) +- [ ] ArgoCD installed on cluster +- [ ] App synced from Git repo to K8s +- [ ] Auto-sync on push to main + +## Phase 9: Security & Quality (`security` branch) +- [ ] Trivy image scan in CI pipeline +- [ ] SonarQube code quality scan +- [ ] OWASP dependency check +- [ ] Non-root container user + +## Phase 10: AI Chatbot (`ai` branch) +- [ ] Ollama container in docker-compose (self-hosted, zero cost) +- [ ] Chat REST API in Spring Boot calling Ollama +- [ ] Floating chat widget on dashboard +- [ ] Context-aware — knows user's balance and transactions +- [ ] Deploy Ollama on K8s with GPU/CPU resource limits + +## Phase 11: Production Readiness (`prod` branch) +- [ ] TLS / HTTPS via cert-manager +- [ ] Horizontal Pod Autoscaler (HPA) +- [ ] Resource limits and requests +- [ ] Liveness & readiness probes +- [ ] Multi-environment setup (dev / staging / prod) + +--- + +## The Story for Interviews + +> "I took a Spring Boot banking application, integrated a self-hosted AI chatbot +> using Ollama, containerized everything with Docker, built a CI/CD pipeline with +> GitHub Actions, deployed to Kubernetes using Helm charts, provisioned cloud +> infrastructure with Terraform, set up monitoring with Prometheus and Grafana, +> and implemented GitOps with ArgoCD for automated deployments." + +Each phase = one branch = one talking point. From b3f473a096a2907606cbd3c455725915457e61f9 Mon Sep 17 00:00:00 2001 From: Ubuntu Date: Mon, 30 Mar 2026 13:32:48 +0000 Subject: [PATCH 03/32] added docker publish workflow --- .env | 10 +++++++ .github/workflows/docker-publish.yml | 35 +++++++++++++++++++++++ Dockerfile | 32 +++++++++++++++++++++ src/main/resources/application.properties | 4 +-- 4 files changed, 79 insertions(+), 2 deletions(-) create mode 100644 .env create mode 100644 .github/workflows/docker-publish.yml create mode 100644 Dockerfile diff --git a/.env b/.env new file mode 100644 index 00000000..b1c1b7f6 --- /dev/null +++ b/.env @@ -0,0 +1,10 @@ +# App configuration +APP_PORT=8083 + +# MySQL configuration +MYSQL_HOST=mysql-db +MYSQL_PORT=3306 +MYSQL_ROOT_PASSWORD=root123 +MYSQL_DATABASE=bankappdb +MYSQL_USER=bankuser +MYSQL_PASSWORD=root123 diff --git a/.github/workflows/docker-publish.yml b/.github/workflows/docker-publish.yml new file mode 100644 index 00000000..0a34fbdc --- /dev/null +++ b/.github/workflows/docker-publish.yml @@ -0,0 +1,35 @@ +name: Docker Build & Push + +on: + push: + branches: [main] + +jobs: + build-&-push: + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Build Jar + run: mvn clean package + + - name: Login to Docker hub + uses: docker/login-action@v3 + with: + username: ${{secrets.DOCKER_USERNAME}} + password: ${{secrets.DOCKER_TOKEN}} + + - name: Build & Push to DockerHub + uses: docker/build-push-action@v5 + with: + context: . + push: true + tags: | + + ${{vars.DOCKER_USERNAME}}/ai-bankapp:${{github.ref_name}} + ${{vars.DOCKER_USERNAME}}/ai-bankapp:latest + ${{vars.DOCKER_USERNAME}}/ai-bankapp:${{github.sha}} + + diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 00000000..ab3fa12f --- /dev/null +++ b/Dockerfile @@ -0,0 +1,32 @@ +# ---------- Stage 1: Build ---------- +FROM maven:3.9.9-eclipse-temurin-21 AS builder + +WORKDIR /build + +# Copy project files +COPY pom.xml . +COPY src ./src + +# Build the application +RUN mvn clean package -DskipTests + + +# ---------- Stage 2: Runtime ---------- +FROM eclipse-temurin:21-jdk-alpine + +# Create non-root user +RUN addgroup -S springgroup && adduser -S springuser -G springgroup + +WORKDIR /app + +# Copy jar from builder stage +COPY --chown=springuser:springgroup --from=builder /build/target/*.jar app.jar + + + +# Switch to non-root user +USER springuser + +EXPOSE 8083 + +ENTRYPOINT ["java","-jar","app.jar"] diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index f934de60..a41bda70 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -1,9 +1,9 @@ spring.application.name=bankapp # MySQL Database configuration -spring.datasource.url=jdbc:mysql://${MYSQL_HOST:localhost}:${MYSQL_PORT:3306}/${MYSQL_DATABASE:bankappdb}?useSSL=false&allowPublicKeyRetrieval=true +spring.datasource.url=jdbc:mysql://${MYSQL_HOST:msql-db}:${MYSQL_PORT:3306}/${MYSQL_DATABASE:bankappdb}?useSSL=false&allowPublicKeyRetrieval=true spring.datasource.username=${MYSQL_USER:root} -spring.datasource.password=${MYSQL_PASSWORD:Test@123} +spring.datasource.password=${MYSQL_PASSWORD:root123} spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver # JPA & Hibernate configuration From a6a0ddae67c8745121438b637166a2c455e96d2a Mon Sep 17 00:00:00 2001 From: Ubuntu Date: Mon, 30 Mar 2026 13:34:43 +0000 Subject: [PATCH 04/32] Fixed error in docker publish workflow --- .github/workflows/docker-publish.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/docker-publish.yml b/.github/workflows/docker-publish.yml index 0a34fbdc..5926f0c5 100644 --- a/.github/workflows/docker-publish.yml +++ b/.github/workflows/docker-publish.yml @@ -5,7 +5,7 @@ on: branches: [main] jobs: - build-&-push: + build-and-push: runs-on: ubuntu-latest steps: From cc2aef4515e7770a49d432be0b2e11ad2f58b92b Mon Sep 17 00:00:00 2001 From: Ubuntu Date: Mon, 30 Mar 2026 13:41:21 +0000 Subject: [PATCH 05/32] Fixed error in docker publish workflow --- .github/workflows/docker-publish.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/docker-publish.yml b/.github/workflows/docker-publish.yml index 5926f0c5..966f40c8 100644 --- a/.github/workflows/docker-publish.yml +++ b/.github/workflows/docker-publish.yml @@ -2,7 +2,7 @@ name: Docker Build & Push on: push: - branches: [main] + branches: [main, origin, start] jobs: build-and-push: From 786d4dd57ca3a13971cf5f8134bd2ed380b2e4a2 Mon Sep 17 00:00:00 2001 From: Ubuntu Date: Mon, 30 Mar 2026 13:50:30 +0000 Subject: [PATCH 06/32] Fixed error in docker publish workflow --- .github/workflows/docker-publish.yml | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/.github/workflows/docker-publish.yml b/.github/workflows/docker-publish.yml index 966f40c8..d119e8e6 100644 --- a/.github/workflows/docker-publish.yml +++ b/.github/workflows/docker-publish.yml @@ -2,16 +2,22 @@ name: Docker Build & Push on: push: - branches: [main, origin, start] + branches: [main] jobs: - build-and-push: + build-&-push: runs-on: ubuntu-latest steps: - name: Checkout code uses: actions/checkout@v4 + - name: Set up Java + uses: actions/setup-java@v4 + with: + distribution: 'temurin' + java-version: '21' + - name: Build Jar run: mvn clean package @@ -31,5 +37,3 @@ jobs: ${{vars.DOCKER_USERNAME}}/ai-bankapp:${{github.ref_name}} ${{vars.DOCKER_USERNAME}}/ai-bankapp:latest ${{vars.DOCKER_USERNAME}}/ai-bankapp:${{github.sha}} - - From 3c217b76ccbfed13c597a3d0acc53593b5996bc3 Mon Sep 17 00:00:00 2001 From: Ubuntu Date: Mon, 30 Mar 2026 14:20:35 +0000 Subject: [PATCH 07/32] Fixed error in docker publish workflow --- .github/workflows/docker-publish.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/docker-publish.yml b/.github/workflows/docker-publish.yml index d119e8e6..27052260 100644 --- a/.github/workflows/docker-publish.yml +++ b/.github/workflows/docker-publish.yml @@ -5,7 +5,7 @@ on: branches: [main] jobs: - build-&-push: + build-and-push: runs-on: ubuntu-latest steps: From 795e61463d77bc99c8253b4944c7d97458e9bf9b Mon Sep 17 00:00:00 2001 From: Ubuntu Date: Mon, 30 Mar 2026 14:30:19 +0000 Subject: [PATCH 08/32] Added workflow-dispatch in docker publish workflow --- .github/workflows/docker-publish.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/docker-publish.yml b/.github/workflows/docker-publish.yml index 27052260..bc7d7398 100644 --- a/.github/workflows/docker-publish.yml +++ b/.github/workflows/docker-publish.yml @@ -3,6 +3,7 @@ name: Docker Build & Push on: push: branches: [main] + workflow_dispatch: jobs: build-and-push: From 155713ce9c4e199703de1797fe51e38375b4bd36 Mon Sep 17 00:00:00 2001 From: Ubuntu Date: Mon, 30 Mar 2026 14:35:10 +0000 Subject: [PATCH 09/32] Fixed error in docker publish workflow --- .github/workflows/docker-publish.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/docker-publish.yml b/.github/workflows/docker-publish.yml index bc7d7398..49099350 100644 --- a/.github/workflows/docker-publish.yml +++ b/.github/workflows/docker-publish.yml @@ -20,7 +20,7 @@ jobs: java-version: '21' - name: Build Jar - run: mvn clean package + run: mvn clean package -DskipTests - name: Login to Docker hub uses: docker/login-action@v3 From ade40fa3521a57f84825fd654b551db93d23dcc1 Mon Sep 17 00:00:00 2001 From: Ubuntu Date: Mon, 30 Mar 2026 14:38:33 +0000 Subject: [PATCH 10/32] Fixed error in docker publish workflow --- .github/workflows/docker-publish.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/docker-publish.yml b/.github/workflows/docker-publish.yml index 49099350..81fcea2b 100644 --- a/.github/workflows/docker-publish.yml +++ b/.github/workflows/docker-publish.yml @@ -20,7 +20,7 @@ jobs: java-version: '21' - name: Build Jar - run: mvn clean package -DskipTests + run: mvn clean package -DskipTests=true - name: Login to Docker hub uses: docker/login-action@v3 From 25b2404496aa778efb74ab953f26976292719ac7 Mon Sep 17 00:00:00 2001 From: Ubuntu Date: Mon, 30 Mar 2026 14:46:36 +0000 Subject: [PATCH 11/32] Fixed error in docker publish workflow --- .github/workflows/docker-publish.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/docker-publish.yml b/.github/workflows/docker-publish.yml index 81fcea2b..31e62b42 100644 --- a/.github/workflows/docker-publish.yml +++ b/.github/workflows/docker-publish.yml @@ -20,7 +20,7 @@ jobs: java-version: '21' - name: Build Jar - run: mvn clean package -DskipTests=true + run: mvn clean package -Dmaven.test.skip=true - name: Login to Docker hub uses: docker/login-action@v3 From 194b53d8a41b51bbafb6a950fb10f18f3092ed99 Mon Sep 17 00:00:00 2001 From: Ubuntu Date: Mon, 30 Mar 2026 14:52:18 +0000 Subject: [PATCH 12/32] Fixed error in docker publish workflow --- .github/workflows/docker-publish.yml | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/.github/workflows/docker-publish.yml b/.github/workflows/docker-publish.yml index 31e62b42..47228aa4 100644 --- a/.github/workflows/docker-publish.yml +++ b/.github/workflows/docker-publish.yml @@ -3,7 +3,6 @@ name: Docker Build & Push on: push: branches: [main] - workflow_dispatch: jobs: build-and-push: @@ -22,6 +21,10 @@ jobs: - name: Build Jar run: mvn clean package -Dmaven.test.skip=true + - name: Show Surefire Reports on Failure + if: failure() + run: find . -name "*.txt" -path "*/surefire-reports/*" -exec cat {} \; + - name: Login to Docker hub uses: docker/login-action@v3 with: @@ -38,3 +41,7 @@ jobs: ${{vars.DOCKER_USERNAME}}/ai-bankapp:${{github.ref_name}} ${{vars.DOCKER_USERNAME}}/ai-bankapp:latest ${{vars.DOCKER_USERNAME}}/ai-bankapp:${{github.sha}} + + + + From aa3b9af69da2146ff64ceaa2f6b28b90748e4b5a Mon Sep 17 00:00:00 2001 From: Ubuntu Date: Mon, 30 Mar 2026 15:16:44 +0000 Subject: [PATCH 13/32] updated pom.xml,application properties file fr dockerhub publish workflow --- pom.xml | 5 +++++ .../java/com/example/bankapp/BankappApplicationTests.java | 1 + src/test/resources/application.properties | 8 ++++++++ 3 files changed, 14 insertions(+) create mode 100644 src/test/resources/application.properties diff --git a/pom.xml b/pom.xml index c9942e6c..abe9815e 100644 --- a/pom.xml +++ b/pom.xml @@ -73,6 +73,11 @@ spring-boot-starter-test test + + com.h2database + h2 + test + org.springframework.security spring-security-test diff --git a/src/test/java/com/example/bankapp/BankappApplicationTests.java b/src/test/java/com/example/bankapp/BankappApplicationTests.java index 63c64e9d..990e5fcc 100644 --- a/src/test/java/com/example/bankapp/BankappApplicationTests.java +++ b/src/test/java/com/example/bankapp/BankappApplicationTests.java @@ -4,6 +4,7 @@ import org.springframework.boot.test.context.SpringBootTest; @SpringBootTest +@ActiveProfiles("test") class BankappApplicationTests { @Test diff --git a/src/test/resources/application.properties b/src/test/resources/application.properties new file mode 100644 index 00000000..60ac6892 --- /dev/null +++ b/src/test/resources/application.properties @@ -0,0 +1,8 @@ +spring.datasource.url=jdbc:h2:mem:testdb;DB_CLOSE_DELAY=-1 +spring.datasource.driver-class-name=org.h2.Driver +spring.datasource.username=sa +spring.datasource.password= +spring.jpa.hibernate.ddl-auto=create-drop +spring.jpa.database-platform=org.hibernate.dialect.H2Dialect +spring.security.user.name=test +spring.security.user.password=test From 224efbd407226d5615c61c4d1c9b362659a6f0dc Mon Sep 17 00:00:00 2001 From: Ubuntu Date: Mon, 30 Mar 2026 15:19:54 +0000 Subject: [PATCH 14/32] Fixed error in docker publish workflow --- .github/workflows/docker-publish.yml | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/.github/workflows/docker-publish.yml b/.github/workflows/docker-publish.yml index 47228aa4..557a19ac 100644 --- a/.github/workflows/docker-publish.yml +++ b/.github/workflows/docker-publish.yml @@ -19,11 +19,7 @@ jobs: java-version: '21' - name: Build Jar - run: mvn clean package -Dmaven.test.skip=true - - - name: Show Surefire Reports on Failure - if: failure() - run: find . -name "*.txt" -path "*/surefire-reports/*" -exec cat {} \; + run: mvn clean package - name: Login to Docker hub uses: docker/login-action@v3 From f5a318ae6641251e469ef233ee04ed671f3a2753 Mon Sep 17 00:00:00 2001 From: Ubuntu Date: Mon, 30 Mar 2026 15:22:16 +0000 Subject: [PATCH 15/32] Fixed error in docker publish workflow --- .github/workflows/docker-publish.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/docker-publish.yml b/.github/workflows/docker-publish.yml index 557a19ac..a3d05834 100644 --- a/.github/workflows/docker-publish.yml +++ b/.github/workflows/docker-publish.yml @@ -3,7 +3,7 @@ name: Docker Build & Push on: push: branches: [main] - + workflow_dispatch: jobs: build-and-push: runs-on: ubuntu-latest From 1c1ad64d54a4b8a2f9ec774f8a1b11d08842960b Mon Sep 17 00:00:00 2001 From: Ubuntu Date: Mon, 30 Mar 2026 15:31:19 +0000 Subject: [PATCH 16/32] fix: add missing ActiveProfiles import in test class --- src/test/java/com/example/bankapp/BankappApplicationTests.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/test/java/com/example/bankapp/BankappApplicationTests.java b/src/test/java/com/example/bankapp/BankappApplicationTests.java index 990e5fcc..6184e848 100644 --- a/src/test/java/com/example/bankapp/BankappApplicationTests.java +++ b/src/test/java/com/example/bankapp/BankappApplicationTests.java @@ -2,6 +2,7 @@ import org.junit.jupiter.api.Test; import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.ActiveProfiles; @SpringBootTest @ActiveProfiles("test") From 97737f30723cfaffde8c0ea43a9fe904361d5c51 Mon Sep 17 00:00:00 2001 From: Ubuntu Date: Mon, 30 Mar 2026 15:56:57 +0000 Subject: [PATCH 17/32] Fixed error in docker-publish workflow --- .github/workflows/docker-publish.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/docker-publish.yml b/.github/workflows/docker-publish.yml index a3d05834..bec1a7e5 100644 --- a/.github/workflows/docker-publish.yml +++ b/.github/workflows/docker-publish.yml @@ -34,9 +34,9 @@ jobs: push: true tags: | - ${{vars.DOCKER_USERNAME}}/ai-bankapp:${{github.ref_name}} - ${{vars.DOCKER_USERNAME}}/ai-bankapp:latest - ${{vars.DOCKER_USERNAME}}/ai-bankapp:${{github.sha}} + ${{secrets.DOCKER_USERNAME}}/ai-bankapp:${{github.ref_name}} + ${{secrets.DOCKER_USERNAME}}/ai-bankapp:latest + ${{secrets.DOCKER_USERNAME}}/ai-bankapp:${{github.sha}} From 0346b407e0c4e15a4f9d939845fdbbfc0b26f2f9 Mon Sep 17 00:00:00 2001 From: Ubuntu Date: Wed, 1 Apr 2026 12:17:14 +0000 Subject: [PATCH 18/32] Updated the docker publish workflow to push only on main branch --- .github/workflows/docker-publish.yml | 71 +++++++++++++--------------- 1 file changed, 34 insertions(+), 37 deletions(-) diff --git a/.github/workflows/docker-publish.yml b/.github/workflows/docker-publish.yml index bec1a7e5..03382c18 100644 --- a/.github/workflows/docker-publish.yml +++ b/.github/workflows/docker-publish.yml @@ -2,42 +2,39 @@ name: Docker Build & Push on: push: - branches: [main] - workflow_dispatch: -jobs: - build-and-push: - runs-on: ubuntu-latest - - steps: - - name: Checkout code - uses: actions/checkout@v4 - - - name: Set up Java - uses: actions/setup-java@v4 - with: - distribution: 'temurin' - java-version: '21' - - - name: Build Jar - run: mvn clean package - - - name: Login to Docker hub - uses: docker/login-action@v3 - with: - username: ${{secrets.DOCKER_USERNAME}} - password: ${{secrets.DOCKER_TOKEN}} + branches: ["*"] # allow all branches so we can test skip logic + workflow_dispatch: - - name: Build & Push to DockerHub - uses: docker/build-push-action@v5 - with: - context: . - push: true - tags: | - - ${{secrets.DOCKER_USERNAME}}/ai-bankapp:${{github.ref_name}} - ${{secrets.DOCKER_USERNAME}}/ai-bankapp:latest - ${{secrets.DOCKER_USERNAME}}/ai-bankapp:${{github.sha}} - - - +jobs: + build-and-push: + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Set up Java + uses: actions/setup-java@v4 + with: + distribution: 'temurin' + java-version: '21' + + - name: Build Jar + run: mvn clean package + + - name: Login to Docker Hub + if: github.ref == 'refs/heads/main' + uses: docker/login-action@v3 + with: + username: ${{ secrets.DOCKER_USERNAME }} + password: ${{ secrets.DOCKER_TOKEN }} + + - name: Build & Push to DockerHub + uses: docker/build-push-action@v5 + with: + context: . + push: ${{ github.ref == 'refs/heads/main' }} + tags: | + ${{ secrets.DOCKER_USERNAME }}/ai-bankapp:latest + ${{ secrets.DOCKER_USERNAME }}/ai-bankapp:sha-${{ github.sha }} From c49d9ec2cabe6f7bc329cce91c2adb9f03c682aa Mon Sep 17 00:00:00 2001 From: Sana Shaik Date: Thu, 2 Apr 2026 14:51:46 +0530 Subject: [PATCH 19/32] Update README with Docker badge and feature details Added Docker Build & Push badge and updated features section. --- README.md | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index fa4154b3..256ef7ed 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,10 @@ # AI-BankApp-DevOps +![Docker Build & Push](https://github.com/Sana-2026/AI-BankApp-DevOps/actions/workflows/docker-publish.yml/badge.svg) + A Spring Boot banking application used as a base for learning end-to-end DevOps — from Docker to Kubernetes to GitOps. ## Tech Stack - - **Backend:** Spring Boot 3.4.1, Java 21, Spring Security, JPA/Hibernate - **Frontend:** Thymeleaf, Bootstrap 5, Glassmorphism UI with dark/light theme - **Database:** MySQL 8.0 @@ -11,7 +12,6 @@ A Spring Boot banking application used as a base for learning end-to-end DevOps - **DevOps:** Docker, GitHub Actions, Kubernetes, Helm, Terraform, Prometheus, Grafana, ArgoCD ## Branches - | Branch | Description | |--------|-------------| | `start` | Modernized app — full backend + frontend (developer handoff) | @@ -24,44 +24,34 @@ See [ROADMAP.md](ROADMAP.md) for the full progression. ## Quick Start ### Run locally (needs Java 21 + MySQL) - ```bash # Create database mysql -u root -p -e "CREATE DATABASE bankappdb;" - # Run the app ./mvnw spring-boot:run ``` ### Run with Docker (recommended) - ```bash # Switch to docker branch git checkout docker - # Start everything docker compose up -d --build - # Visit http://localhost:8080 ``` ### Run with AI Chatbot - ```bash # Switch to ai branch git checkout ai - # Start everything (includes Ollama) docker compose up -d --build - # Pull the AI model (one-time) docker exec bankapp-ollama ollama pull tinyllama - # Visit http://localhost:8080 ``` ## Features - - User registration & login with BCrypt passwords - Deposit, withdraw, transfer between accounts - Transaction history with color-coded entries From b76e097ca3d1a1fbe232915e448436016f7e2303 Mon Sep 17 00:00:00 2001 From: Sana Shaik Date: Sat, 11 Apr 2026 23:26:05 +0530 Subject: [PATCH 20/32] Update README.md --- README.md | 53 +++++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 45 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 256ef7ed..d38ffa76 100644 --- a/README.md +++ b/README.md @@ -2,14 +2,34 @@ ![Docker Build & Push](https://github.com/Sana-2026/AI-BankApp-DevOps/actions/workflows/docker-publish.yml/badge.svg) -A Spring Boot banking application used as a base for learning end-to-end DevOps — from Docker to Kubernetes to GitOps. +A production-ready full-stack banking application built with Spring Boot and MySQL, featuring secure authentication, account management, and transaction processing. Integrated with a complete DevOps pipeline using Docker, GitHub Actions CI/CD, Kubernetes, Helm, Terraform, Prometheus, and ArgoCD for scalable, automated, and observable deployments. -## Tech Stack -- **Backend:** Spring Boot 3.4.1, Java 21, Spring Security, JPA/Hibernate -- **Frontend:** Thymeleaf, Bootstrap 5, Glassmorphism UI with dark/light theme -- **Database:** MySQL 8.0 -- **AI:** Ollama (self-hosted LLM chatbot, zero cost) -- **DevOps:** Docker, GitHub Actions, Kubernetes, Helm, Terraform, Prometheus, Grafana, ArgoCD +dashboard-app + +## 🏗️ Architecture + +A cloud-native AI banking application built with Spring Boot, containerized using Docker, and deployed via GitHub Actions CI/CD. It leverages Kubernetes for scalability, ArgoCD for GitOps, and Prometheus & Grafana for monitoring. + +![architecture](https://github.com/user-attachments/assets/3daa07a3-51b6-4918-b87f-63cb48d7a278) + +--- +## 🧰 Tech Stack + +| Category | Technologies | +|----------------|-------------| +| **Backend** | Spring Boot 3.4.1, Java 21, Spring Security, JPA/Hibernate | +| **Frontend** | Thymeleaf, Bootstrap 5, Glassmorphism UI (Dark/Light Theme) | +| **Database** | MySQL 8.0 | +| **AI** | Ollama (Self-hosted LLM chatbot) | +| **Containerization** | Docker | +| **CI/CD** | GitHub Actions | +| **Orchestration** | Kubernetes | +| **Package Management** | Helm | +| **Infrastructure as Code** | Terraform | +| **Monitoring** | Prometheus, Grafana | +| **GitOps** | ArgoCD | + +--- ## Branches | Branch | Description | @@ -19,7 +39,6 @@ A Spring Boot banking application used as a base for learning end-to-end DevOps | `ai` | Adds AI chatbot powered by Ollama | | `main` | End-to-end DevOps (WIP) | -See [ROADMAP.md](ROADMAP.md) for the full progression. ## Quick Start @@ -59,3 +78,21 @@ docker exec bankapp-ollama ollama pull tinyllama - AI chatbot that knows your balance and recent transactions - Prometheus metrics at `/actuator/prometheus` - Health check at `/actuator/health` + +- ## 🚀 Future Enhancements + +* Kubernetes auto-deployment with Helm +* GitOps using ArgoCD +* Terraform-based infrastructure provisioning +* Advanced AI banking features + +--- + +## 👨‍💻 Author + +Sana Shaik + +--- + +🔥 Built as part of DevOps Capstone Project + From c8b7aacf0940c346ae08a6e4c41073838a73b655 Mon Sep 17 00:00:00 2001 From: Ubuntu Date: Sat, 11 Apr 2026 17:59:33 +0000 Subject: [PATCH 21/32] Added the test script --- test.sh | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100755 test.sh diff --git a/test.sh b/test.sh new file mode 100755 index 00000000..30d8f9a0 --- /dev/null +++ b/test.sh @@ -0,0 +1,13 @@ +#!/bin/bash + +echo "Testing Bank AI App..." + +response=$(curl -s http://localhost:8080/actuator/health) + +if [[ "$response" == *"UP"* ]]; then + echo "✅ Test Passed" + exit 0 +else + echo "❌ Test Failed" + exit 1 +fi From 4db298d55e24f2d4e798864b0a15db64bcd91f4a Mon Sep 17 00:00:00 2001 From: Ubuntu Date: Sat, 11 Apr 2026 18:53:23 +0000 Subject: [PATCH 22/32] Added reusable build and test workflow --- .github/workflows/reusable-build-test.yml | 49 +++++++++++++++++++++++ 1 file changed, 49 insertions(+) create mode 100644 .github/workflows/reusable-build-test.yml diff --git a/.github/workflows/reusable-build-test.yml b/.github/workflows/reusable-build-test.yml new file mode 100644 index 00000000..005e72a5 --- /dev/null +++ b/.github/workflows/reusable-build-test.yml @@ -0,0 +1,49 @@ +name: Reusable Build Test Workflow +on: + workflow_call: + inputs: + java-version: + description: 'The version of Java to use for the build' + required: true + type: string + runs_tests: + description: 'Whether to run tests' + required: false + type: boolean + default: true + outputs: + test-result: + description: 'The result of the test process' + value: ${{ jobs.build.outputs.test-result }} +jobs: + build: + runs-on: ubuntu-latest + outputs: + test-result: ${{ steps.test.outputs.result }} + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Set up Java ${{ inputs.java-version }} + uses: actions/setup-java@v4 + with: + java-version: ${{ inputs.java-version }} + distribution: 'temurin' + + - name: Build with maven + run: mvn clean install -DskipTests + + - name: Run test script + id: test + if: ${{ inputs.runs_tests }} + run: | + chmod +x ./test.sh + if ./test.sh; then + + echo "result=passed" >> $GITHUB_OUTPUT + else + + echo "result=failed" >> $GITHUB_OUTPUT + fi + + From 9283013c1a239d088855c17a4b911ed707386908 Mon Sep 17 00:00:00 2001 From: Ubuntu Date: Sun, 12 Apr 2026 16:05:07 +0000 Subject: [PATCH 23/32] Added reusable docker workflow --- .github/workflows/reusable-docker.yml | 52 +++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) create mode 100644 .github/workflows/reusable-docker.yml diff --git a/.github/workflows/reusable-docker.yml b/.github/workflows/reusable-docker.yml new file mode 100644 index 00000000..b4c6b898 --- /dev/null +++ b/.github/workflows/reusable-docker.yml @@ -0,0 +1,52 @@ +name: Reusable Docker Workflow +on: + workflow_call: + inputs: + image-name: + description: 'The name of the Docker image to build' + required: true + type: string + image-tag: + description: 'The tag of the built Docker image' + required: false + type: string + default: 'latest' + + secrets: + + DOCKER_USERNAME: + description: 'DockerHub username' + required: true + DOCKER_TOKEN: + description: 'DockerHub access token' + required: true + + outputs: + image_url: + description: 'The URL of the built Docker image' + value: ${{ jobs.DockerBuild.outputs.image_url }} + +jobs: + DockerBuild: + runs-on: ubuntu-latest + outputs: + image_url: ${{ steps.build.outputs.image_url }} + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: login to DockerHub + uses: docker/login-action@v4 + with: + username: ${{ secrets.DOCKER_USERNAME }} + password: ${{ secrets.DOCKER_TOKEN }} + + - name: Build and Push Docker image + id: build + run: | + docker build -t ${{ inputs.image-name }}:${{ inputs.image-tag }} . + docker push ${{ inputs.image-name }}:${{ inputs.image-tag }} + echo "image-url=${{ inputs.image-name }}:${{ inputs.image-tag }}" >> $GITHUB_OUTPUT + + + From 31a3c005458402332ad68c55fd5ad84fd6eccd0b Mon Sep 17 00:00:00 2001 From: Ubuntu Date: Sun, 12 Apr 2026 16:36:53 +0000 Subject: [PATCH 24/32] Added the pr pipeline workflow --- .github/workflows/pr-pipeline.yml | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 .github/workflows/pr-pipeline.yml diff --git a/.github/workflows/pr-pipeline.yml b/.github/workflows/pr-pipeline.yml new file mode 100644 index 00000000..631cdf08 --- /dev/null +++ b/.github/workflows/pr-pipeline.yml @@ -0,0 +1,20 @@ +name: PR Pipeline +on: + pull_request: + branches: [main] + types: ['opened', 'synchronize'] + +jobs: + call-reusable-build-test: + uses: ./.github/workflows/reusable-build-test.yml + with: + java-version: '21' + run_tests: true + + pr-comment: + runs-on: ubuntu-latest + needs: call-reusable-build-test + steps: + - name: PR summary + run: | + echo "PR checks passed from branch : ${{ github.head_ref }} " From d94d4174df2a739cd19b672ed719b97bcc8b8d9e Mon Sep 17 00:00:00 2001 From: Ubuntu Date: Sun, 12 Apr 2026 16:40:10 +0000 Subject: [PATCH 25/32] testing PR pipeline --- README.MD | 1 + 1 file changed, 1 insertion(+) create mode 100644 README.MD diff --git a/README.MD b/README.MD new file mode 100644 index 00000000..fb1f43db --- /dev/null +++ b/README.MD @@ -0,0 +1 @@ +#testing- Pull_request From 5e36230bd17d22c657502de91dbcefbae86632fc Mon Sep 17 00:00:00 2001 From: Ubuntu Date: Mon, 13 Apr 2026 15:22:03 +0000 Subject: [PATCH 26/32] testing PR pipeline --- README.MD | 1 + 1 file changed, 1 insertion(+) diff --git a/README.MD b/README.MD index fb1f43db..3db10f69 100644 --- a/README.MD +++ b/README.MD @@ -1 +1,2 @@ #testing- Pull_request +#testing1- Pull_request From c71fdf2b74ee430fe1bed0da7628943f35cfebff Mon Sep 17 00:00:00 2001 From: Ubuntu Date: Mon, 13 Apr 2026 15:30:38 +0000 Subject: [PATCH 27/32] Updated PR pipeline --- .github/workflows/pr-pipeline.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/pr-pipeline.yml b/.github/workflows/pr-pipeline.yml index 631cdf08..5edf418b 100644 --- a/.github/workflows/pr-pipeline.yml +++ b/.github/workflows/pr-pipeline.yml @@ -1,7 +1,7 @@ name: PR Pipeline on: pull_request: - branches: [main] + branches: [start] types: ['opened', 'synchronize'] jobs: From 1062d3e4a6b8a9ce7ed6688ba537c2a3afc03800 Mon Sep 17 00:00:00 2001 From: Ubuntu Date: Mon, 13 Apr 2026 15:32:22 +0000 Subject: [PATCH 28/32] Testing PR pipeline --- README.MD | 1 + 1 file changed, 1 insertion(+) create mode 100644 README.MD diff --git a/README.MD b/README.MD new file mode 100644 index 00000000..9308ad17 --- /dev/null +++ b/README.MD @@ -0,0 +1 @@ +#testing2- Pull_request From 27927df96c6bf4cfe7b75be03c44970b0cb9eae2 Mon Sep 17 00:00:00 2001 From: Ubuntu Date: Mon, 13 Apr 2026 15:38:02 +0000 Subject: [PATCH 29/32] Testing PR pipeline --- README.MD | 1 + 1 file changed, 1 insertion(+) diff --git a/README.MD b/README.MD index 9308ad17..c5a238f8 100644 --- a/README.MD +++ b/README.MD @@ -1 +1,2 @@ #testing2- Pull_request +#testing3- Pull_request From abc76f3c723ddf90437547cf437449589826fc90 Mon Sep 17 00:00:00 2001 From: Ubuntu Date: Mon, 13 Apr 2026 16:40:57 +0000 Subject: [PATCH 30/32] trigger PR pipeline again --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index d38ffa76..7bb78c1f 100644 --- a/README.md +++ b/README.md @@ -96,3 +96,4 @@ Sana Shaik 🔥 Built as part of DevOps Capstone Project +Final trigger From 10c112fd0eccd72015709036a3f24ae59f46cffa Mon Sep 17 00:00:00 2001 From: Ubuntu Date: Mon, 13 Apr 2026 18:01:10 +0000 Subject: [PATCH 31/32] test: trigger PR pipeline --- FETCH_HEAD | 0 README.md | 1 + 2 files changed, 1 insertion(+) create mode 100644 FETCH_HEAD diff --git a/FETCH_HEAD b/FETCH_HEAD new file mode 100644 index 00000000..e69de29b diff --git a/README.md b/README.md index 1f5e49ff..e3569c1b 100644 --- a/README.md +++ b/README.md @@ -99,3 +99,4 @@ Sana Shaik <<<<<<< HEAD Final trigger ======= +Testing my PR 10th time From 5bb31c83036378f8c04ce967d7cf09a8ba22f923 Mon Sep 17 00:00:00 2001 From: Ubuntu Date: Mon, 13 Apr 2026 18:14:08 +0000 Subject: [PATCH 32/32] Fixed error in Pr pipeline workflow --- .github/workflows/pr-pipeline.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/pr-pipeline.yml b/.github/workflows/pr-pipeline.yml index 5edf418b..8c7103dd 100644 --- a/.github/workflows/pr-pipeline.yml +++ b/.github/workflows/pr-pipeline.yml @@ -9,7 +9,7 @@ jobs: uses: ./.github/workflows/reusable-build-test.yml with: java-version: '21' - run_tests: true + runs_tests: true pr-comment: runs-on: ubuntu-latest