From f32d72d5e7a99ba01dd3ac65a23ff96d1dc08da5 Mon Sep 17 00:00:00 2001 From: "erique.rocha" Date: Wed, 5 Nov 2025 11:03:54 -0300 Subject: [PATCH 1/3] feat: beginning of the transition to no longer using fixed templates --- Cargo.lock | 191 ---------- Cargo.toml | 2 - src/main.rs | 337 ++---------------- template/3layer/.gitattributes | 2 - template/3layer/.gitignore | 33 -- template/3layer/application/pom.xml | 23 -- .../ports/repositories/AdminRepository.java | 11 - .../repositories/UserEntityRepository.java | 11 - .../ports/services/PasswordService.java | 6 - .../ports/services/TokenService.java | 8 - .../usecases/AuthenticateAdminUseCase.java | 48 --- .../AuthenticateUserEntityUseCase.java | 48 --- .../usecases/CreateUserEntityUseCase.java | 30 -- .../src/main/resources/application.properties | 1 - .../java/dev/falae/api/ApplicationTests.java | 13 - template/3layer/core/pom.xml | 18 - .../domain/core/domain/entities/Admin.java | 98 ----- .../core/domain/entities/UserEntity.java | 98 ----- .../valueobjects/AuthenticationResult.java | 31 -- .../src/main/resources/application.properties | 1 - .../java/dev/falae/api/ApplicationTests.java | 13 - template/3layer/infrastructure/pom.xml | 89 ----- .../AppNameCleanApplication.java | 23 -- .../repositories/JpaAdminRepository.java | 59 --- .../repositories/JpaUserEntityRepository.java | 59 --- .../repositories/entities/AdminEntity.java | 88 ----- .../entities/UserEntityEntity.java | 88 ----- .../repositories/jpa/AdminJpaRepository.java | 12 - .../jpa/UserEntityJpaRepository.java | 12 - .../services/BCryptPasswordService.java | 24 -- .../adapters/services/JwtTokenService.java | 81 ----- .../web/controllers/AuthController.java | 97 ----- .../web/controllers/TestController.java | 26 -- .../web/controllers/UserEntityController.java | 53 --- .../web/dto/CreateUserEntityRequest.java | 52 --- .../web/dto/CreateUserEntityResponse.java | 49 --- .../adapters/web/dto/LoginRequest.java | 29 -- .../adapters/web/dto/LoginResponse.java | 49 --- .../config/BeanConfiguration.java | 38 -- .../config/doc/OpenAPI30Configuration.java | 45 --- .../security/JwtAuthenticationFilter.java | 60 ---- .../config/security/SecurityConfig.java | 42 --- .../src/main/resources/application.properties | 25 -- .../postgres/V01_01__insert_data_test.sql | 32 -- .../java/dev/falae/api/ApplicationTests.java | 13 - template/3layer/mvnw | 295 --------------- template/3layer/mvnw.cmd | 189 ---------- template/3layer/pom.xml | 36 -- 48 files changed, 36 insertions(+), 2652 deletions(-) delete mode 100644 template/3layer/.gitattributes delete mode 100644 template/3layer/.gitignore delete mode 100644 template/3layer/application/pom.xml delete mode 100644 template/3layer/application/src/main/java/your/domain/application/ports/repositories/AdminRepository.java delete mode 100644 template/3layer/application/src/main/java/your/domain/application/ports/repositories/UserEntityRepository.java delete mode 100644 template/3layer/application/src/main/java/your/domain/application/ports/services/PasswordService.java delete mode 100644 template/3layer/application/src/main/java/your/domain/application/ports/services/TokenService.java delete mode 100644 template/3layer/application/src/main/java/your/domain/application/usecases/AuthenticateAdminUseCase.java delete mode 100644 template/3layer/application/src/main/java/your/domain/application/usecases/AuthenticateUserEntityUseCase.java delete mode 100644 template/3layer/application/src/main/java/your/domain/application/usecases/CreateUserEntityUseCase.java delete mode 100644 template/3layer/application/src/main/resources/application.properties delete mode 100644 template/3layer/application/src/test/java/dev/falae/api/ApplicationTests.java delete mode 100644 template/3layer/core/pom.xml delete mode 100644 template/3layer/core/src/main/java/your/domain/core/domain/entities/Admin.java delete mode 100644 template/3layer/core/src/main/java/your/domain/core/domain/entities/UserEntity.java delete mode 100644 template/3layer/core/src/main/java/your/domain/core/domain/valueobjects/AuthenticationResult.java delete mode 100644 template/3layer/core/src/main/resources/application.properties delete mode 100644 template/3layer/core/src/test/java/dev/falae/api/ApplicationTests.java delete mode 100644 template/3layer/infrastructure/pom.xml delete mode 100644 template/3layer/infrastructure/src/main/java/your/domain/infrastructure/AppNameCleanApplication.java delete mode 100644 template/3layer/infrastructure/src/main/java/your/domain/infrastructure/adapters/repositories/JpaAdminRepository.java delete mode 100644 template/3layer/infrastructure/src/main/java/your/domain/infrastructure/adapters/repositories/JpaUserEntityRepository.java delete mode 100644 template/3layer/infrastructure/src/main/java/your/domain/infrastructure/adapters/repositories/entities/AdminEntity.java delete mode 100644 template/3layer/infrastructure/src/main/java/your/domain/infrastructure/adapters/repositories/entities/UserEntityEntity.java delete mode 100644 template/3layer/infrastructure/src/main/java/your/domain/infrastructure/adapters/repositories/jpa/AdminJpaRepository.java delete mode 100644 template/3layer/infrastructure/src/main/java/your/domain/infrastructure/adapters/repositories/jpa/UserEntityJpaRepository.java delete mode 100644 template/3layer/infrastructure/src/main/java/your/domain/infrastructure/adapters/services/BCryptPasswordService.java delete mode 100644 template/3layer/infrastructure/src/main/java/your/domain/infrastructure/adapters/services/JwtTokenService.java delete mode 100644 template/3layer/infrastructure/src/main/java/your/domain/infrastructure/adapters/web/controllers/AuthController.java delete mode 100644 template/3layer/infrastructure/src/main/java/your/domain/infrastructure/adapters/web/controllers/TestController.java delete mode 100644 template/3layer/infrastructure/src/main/java/your/domain/infrastructure/adapters/web/controllers/UserEntityController.java delete mode 100644 template/3layer/infrastructure/src/main/java/your/domain/infrastructure/adapters/web/dto/CreateUserEntityRequest.java delete mode 100644 template/3layer/infrastructure/src/main/java/your/domain/infrastructure/adapters/web/dto/CreateUserEntityResponse.java delete mode 100644 template/3layer/infrastructure/src/main/java/your/domain/infrastructure/adapters/web/dto/LoginRequest.java delete mode 100644 template/3layer/infrastructure/src/main/java/your/domain/infrastructure/adapters/web/dto/LoginResponse.java delete mode 100644 template/3layer/infrastructure/src/main/java/your/domain/infrastructure/config/BeanConfiguration.java delete mode 100644 template/3layer/infrastructure/src/main/java/your/domain/infrastructure/config/doc/OpenAPI30Configuration.java delete mode 100644 template/3layer/infrastructure/src/main/java/your/domain/infrastructure/config/security/JwtAuthenticationFilter.java delete mode 100644 template/3layer/infrastructure/src/main/java/your/domain/infrastructure/config/security/SecurityConfig.java delete mode 100644 template/3layer/infrastructure/src/main/resources/application.properties delete mode 100644 template/3layer/infrastructure/src/main/resources/db/migration/postgres/V01_01__insert_data_test.sql delete mode 100644 template/3layer/infrastructure/src/test/java/dev/falae/api/ApplicationTests.java delete mode 100644 template/3layer/mvnw delete mode 100644 template/3layer/mvnw.cmd delete mode 100644 template/3layer/pom.xml diff --git a/Cargo.lock b/Cargo.lock index a5be0e3..4547c6c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,15 +2,6 @@ # It is not intended for manual editing. version = 4 -[[package]] -name = "aho-corasick" -version = "1.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" -dependencies = [ - "memchr", -] - [[package]] name = "anstream" version = "0.6.21" @@ -61,21 +52,6 @@ dependencies = [ "windows-sys", ] -[[package]] -name = "block-buffer" -version = "0.10.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" -dependencies = [ - "generic-array", -] - -[[package]] -name = "cfg-if" -version = "1.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2fd1289c04a9ea8cb22300a459a72a385d7c73d3259e2ed7dcb2af674838cfa9" - [[package]] name = "clap" version = "4.5.48" @@ -122,52 +98,11 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75" -[[package]] -name = "cpufeatures" -version = "0.2.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" -dependencies = [ - "libc", -] - -[[package]] -name = "crypto-common" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" -dependencies = [ - "generic-array", - "typenum", -] - [[package]] name = "developerStartSpringboot" version = "0.1.0" dependencies = [ "clap", - "regex", - "rust-embed", -] - -[[package]] -name = "digest" -version = "0.10.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" -dependencies = [ - "block-buffer", - "crypto-common", -] - -[[package]] -name = "generic-array" -version = "0.14.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" -dependencies = [ - "typenum", - "version_check", ] [[package]] @@ -182,18 +117,6 @@ version = "1.70.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" -[[package]] -name = "libc" -version = "0.2.176" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "58f929b4d672ea937a23a1ab494143d968337a5f47e56d0815df1e0890ddf174" - -[[package]] -name = "memchr" -version = "2.7.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273" - [[package]] name = "once_cell_polyfill" version = "1.70.1" @@ -218,89 +141,6 @@ dependencies = [ "proc-macro2", ] -[[package]] -name = "regex" -version = "1.11.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b5288124840bee7b386bc413c487869b360b2b4ec421ea56425128692f2a82c" -dependencies = [ - "aho-corasick", - "memchr", - "regex-automata", - "regex-syntax", -] - -[[package]] -name = "regex-automata" -version = "0.4.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "833eb9ce86d40ef33cb1306d8accf7bc8ec2bfea4355cbdebb3df68b40925cad" -dependencies = [ - "aho-corasick", - "memchr", - "regex-syntax", -] - -[[package]] -name = "regex-syntax" -version = "0.8.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "caf4aa5b0f434c91fe5c7f1ecb6a5ece2130b02ad2a590589dda5146df959001" - -[[package]] -name = "rust-embed" -version = "8.7.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "025908b8682a26ba8d12f6f2d66b987584a4a87bc024abc5bbc12553a8cd178a" -dependencies = [ - "rust-embed-impl", - "rust-embed-utils", - "walkdir", -] - -[[package]] -name = "rust-embed-impl" -version = "8.7.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6065f1a4392b71819ec1ea1df1120673418bf386f50de1d6f54204d836d4349c" -dependencies = [ - "proc-macro2", - "quote", - "rust-embed-utils", - "syn", - "walkdir", -] - -[[package]] -name = "rust-embed-utils" -version = "8.7.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6cc0c81648b20b70c491ff8cce00c1c3b223bb8ed2b5d41f0e54c6c4c0a3594" -dependencies = [ - "sha2", - "walkdir", -] - -[[package]] -name = "same-file" -version = "1.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" -dependencies = [ - "winapi-util", -] - -[[package]] -name = "sha2" -version = "0.10.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" -dependencies = [ - "cfg-if", - "cpufeatures", - "digest", -] - [[package]] name = "strsim" version = "0.11.1" @@ -318,12 +158,6 @@ dependencies = [ "unicode-ident", ] -[[package]] -name = "typenum" -version = "1.19.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "562d481066bde0658276a35467c4af00bdc6ee726305698a55b86e61d7ad82bb" - [[package]] name = "unicode-ident" version = "1.0.19" @@ -336,31 +170,6 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" -[[package]] -name = "version_check" -version = "0.9.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" - -[[package]] -name = "walkdir" -version = "2.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" -dependencies = [ - "same-file", - "winapi-util", -] - -[[package]] -name = "winapi-util" -version = "0.1.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" -dependencies = [ - "windows-sys", -] - [[package]] name = "windows-link" version = "0.2.0" diff --git a/Cargo.toml b/Cargo.toml index c7b3515..8ba71f8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,9 +17,7 @@ lto = true codegen-units = 1 [dependencies] -regex = "1.11.3" clap = { version = "4.5.48", features = ["derive"] } -rust-embed = "8" [package.metadata.deb] maintainer = "erique.dev " diff --git a/src/main.rs b/src/main.rs index 2800e00..2dc2760 100644 --- a/src/main.rs +++ b/src/main.rs @@ -10,13 +10,11 @@ * See the LICENSE file for more details. */ -use std::collections::HashMap; -use std::fs; use std::io::{self, Write}; -use std::path::{Path, PathBuf}; -use regex::Regex; +use std::fs; use clap::{Parser, Subcommand}; -use rust_embed::RustEmbed; +use std::path::Path; + const BANNER: &str = concat!("\x1b[32m", r#" developerStartSpringboot @@ -55,32 +53,23 @@ enum Commands { Version, } - -#[derive(RustEmbed)] -#[folder = "template/3layer"] -struct Templates; - -fn extract_embedded_template(dst_root: &Path) -> std::io::Result<()> { - for file in Templates::iter() { - let rel = file.as_ref(); - if let Some(data) = Templates::get(rel) { - let target = dst_root.join(rel); - if let Some(parent) = target.parent() { - std::fs::create_dir_all(parent)?; - } - std::fs::write(&target, data.data.as_ref())?; - } - } +fn init_project() -> io::Result<()> { + println!("{}", BANNER); + let project_name = prompt_required("Project name (e.g.: myAPI)"); + let project_name = to_pascal_case(&project_name); + create_path(&project_name)?; + println!("crated folder {}", project_name); Ok(()) } -fn prompt(label: &str, default: Option<&str>) -> String { - let mut input = String::new(); - print!("{}{}: ", label, default.map(|d| format!(" [{}]", d)).unwrap_or_default()); - io::stdout().flush().unwrap(); - io::stdin().read_line(&mut input).unwrap(); - let s = input.trim().to_string(); - if s.is_empty() { default.unwrap_or("").to_string() } else { s } +fn create_path(name: &str) -> io::Result<()> { + let path = Path::new(name); + if path.exists() { + println!("'{}' already exists", name); + } else { + fs::create_dir(path)?; + } + Ok(()) } fn prompt_required(label: &str) -> String { @@ -93,285 +82,31 @@ fn prompt_required(label: &str) -> String { } } -fn to_snake_case(s: &str) -> String { - let mut out = String::new(); - let mut prev_is_lower_or_digit = false; - - for ch in s.chars() { - if ch.is_alphanumeric() { - if ch.is_uppercase() { - if prev_is_lower_or_digit && !out.is_empty() { - out.push('_'); - } - for lc in ch.to_lowercase() { - out.push(lc); - } - prev_is_lower_or_digit = false; - } else { - out.push(ch); - prev_is_lower_or_digit = true; - } - } else { - if !out.ends_with('_') && !out.is_empty() { - out.push('_'); - } - prev_is_lower_or_digit = false; - } - } - - let norm = out - .trim_matches('_') - .split('_') - .filter(|seg| !seg.is_empty()) - .collect::>() - .join("_"); - - norm -} - -fn to_pascal_case(s: &str) -> String { - let parts = s.split(|c: char| !c.is_alphanumeric()) - .filter(|p| !p.is_empty()); - let mut out = String::new(); - for p in parts { - let mut chs = p.chars(); - if let Some(first) = chs.next() { - out.push_str(&first.to_uppercase().to_string()); - out.push_str(&chs.as_str().to_lowercase()); - } - } - if out.is_empty() { s.to_string() } else { out } -} - -fn to_lower_first(s: &str) -> String { - let mut chars = s.chars(); - match chars.next() { - Some(first) => format!("{}{}", first.to_lowercase(), chars.as_str()), - None => String::new(), +fn prompt(label: &str, default: Option<&str>) -> String { + let mut input = String::new(); + print!("{}{}: ", label, default.map(|d| format!(" [{}]", d)).unwrap_or_default()); + io::stdout().flush().unwrap(); + io::stdin().read_line(&mut input).unwrap(); + let s = input.trim().to_string(); + if s.is_empty() { + default.unwrap_or("").to_string() + } else { + s } } -fn to_app_name_clean(name: &str) -> String { - let tokens: Vec = name - .split(|c: char| !c.is_alphanumeric()) - .filter(|t| !t.is_empty()) - .map(|t| { - let mut chs = t.chars(); - match chs.next() { - Some(first) => format!("{}{}", first.to_uppercase(), chs.as_str().to_lowercase()), +fn to_pascal_case(input: &str) -> String { + input + .split(|c: char| c == ' ' || c == '-' || c == '_') + .filter(|s| !s.is_empty()) + .map(|word| { + let mut chars = word.chars(); + match chars.next() { + Some(first) => first.to_uppercase().collect::() + &chars.as_str().to_lowercase(), None => String::new(), } }) - .collect(); - let mut out = tokens.join(""); - if out.is_empty() { out = "App".to_string(); } - let mut it = out.chars(); - if let Some(f) = it.next() { format!("{}{}", f.to_uppercase(), it.as_str()) } else { out } -} - -fn domain_to_parts(domain: &str) -> Vec { - domain.split('.').filter(|p| !p.is_empty()).map(|s| s.to_string()).collect() -} - -fn is_textual_target(path: &Path) -> bool { - if let Some(ext) = path.extension().and_then(|e| e.to_str()) { - let ext = ext.to_ascii_lowercase(); - return matches!(ext.as_str(), "java" | "properties" | "pom" | "xml" | "sql"); - } - false -} - -fn read_to_string_lossy(path: &Path) -> io::Result { - let bytes = fs::read(path)?; - Ok(String::from_utf8_lossy(&bytes).into_owned()) -} - -fn write_string(path: &Path, content: &str) -> io::Result<()> { - if let Some(parent) = path.parent() { fs::create_dir_all(parent)?; } - fs::write(path, content) -} - -fn replace_placeholders(text: &str, map: &HashMap<&str, String>) -> String { - let mut out = text.to_string(); - for (k, v) in map { - let placeholder = format!("{{{{{}}}}}", k); - out = out.replace(&placeholder, v); - } - out -} - -fn do_content_replacements(text: &str, replacements: &[(Regex, String)]) -> String { - let mut out = text.to_string(); - for (re, rep) in replacements { - out = re.replace_all(&out, rep.as_str()).into_owned(); - } - out -} - -fn walk_all_paths(root: &Path) -> io::Result> { - let mut stack = vec![root.to_path_buf()]; - let mut out = Vec::new(); - while let Some(p) = stack.pop() { - out.push(p.clone()); - if p.is_dir() { - for entry in fs::read_dir(&p)? { - let entry = entry?; - stack.push(entry.path()); - } - } - } - out.sort_by_key(|p| std::cmp::Reverse(p.components().count())); - Ok(out) -} - -fn remove_empty_dirs(root: &Path) -> io::Result<()> { - let all = walk_all_paths(root)?; - for p in all { - if p.is_dir() && p != root { - let _ = fs::remove_dir(&p); - } - } - Ok(()) -} - -fn remap_your_domain_paths(dst_root: &Path, domain_parts: &[String]) -> io::Result<()> { - let all = walk_all_paths(dst_root)?; - let mut files: Vec = all.iter().filter(|p| p.is_file()).cloned().collect(); - - for file in files.drain(..) { - let rel = file.strip_prefix(dst_root).unwrap(); - let comps: Vec = rel.components() - .map(|c| c.as_os_str().to_string_lossy().to_string()) - .collect(); - - let mut i = 0usize; - let mut new_comps: Vec = Vec::new(); - let mut changed = false; - while i < comps.len() { - if i + 1 < comps.len() && comps[i] == "your" && comps[i + 1] == "domain" { - for dp in domain_parts { - new_comps.push(dp.clone()); - } - i += 2; - changed = true; - } else { - new_comps.push(comps[i].clone()); - i += 1; - } - } - - if changed { - let mut new_path = dst_root.to_path_buf(); - for c in new_comps { new_path.push(c); } - if let Some(parent) = new_path.parent() { fs::create_dir_all(parent)?; } - if new_path.exists() { - fs::remove_file(&new_path).ok(); - } - fs::rename(&file, &new_path)?; - } - } - - remove_empty_dirs(dst_root)?; - Ok(()) -} - -fn rename_files_by_tokens(dst_root: &Path, user_entity_pascal: &str, app_name_clean: &str) -> io::Result<()> { - let all = walk_all_paths(dst_root)?; - for path in all.into_iter().filter(|p| p.is_file()) { - let orig_name = path.file_name().and_then(|s| s.to_str()).unwrap_or("").to_string(); - if orig_name.is_empty() { continue; } - let mut new_name = orig_name.clone(); - if new_name.contains("AppNameCleanApplication") { - new_name = new_name.replace("AppNameCleanApplication", &format!("{}Application", app_name_clean)); - } - if new_name.contains("UserEntity") { - new_name = new_name.replace("UserEntity", user_entity_pascal); - } - if new_name != orig_name { - let mut new_path = path.clone(); - new_path.set_file_name(new_name); - if new_path.exists() { - fs::remove_file(&new_path).ok(); - } - fs::rename(&path, &new_path)?; - } - } - Ok(()) -} - -fn edit_file_contents(dst_root: &Path, ph: &HashMap<&str, String>, replacements: &[(Regex, String)]) -> io::Result<()> { - let all = walk_all_paths(dst_root)?; - for path in all.into_iter().filter(|p| p.is_file()) { - if !is_textual_target(&path) { continue; } - let orig = match read_to_string_lossy(&path) { - Ok(s) => s, - Err(_) => continue, - }; - let mut newc = replace_placeholders(&orig, ph); - newc = do_content_replacements(&newc, replacements); - if newc != orig { - write_string(&path, &newc)?; - println!("Project created: {}", path.display()); - } - } - Ok(()) -} - -fn init_project() -> io::Result<()> { - println!("{}", BANNER); - - let app_name = prompt_required("Application name (e.g.: myAPI)"); - let app_name_clean = to_app_name_clean(&app_name); - - let user_entity_input = prompt_required("Enter the name of a user entity (e.g.: UserAccount)"); - let user_entity_pascal = to_pascal_case(&user_entity_input); - let user_entity_lower = to_lower_first(&user_entity_pascal); - let table_name = to_snake_case(&user_entity_pascal); - - let your_domain = prompt_required("Your domain (e.g.: com.example.demo)"); - - let description = prompt("Description", Some("defauil -> api")); - let develop_name = prompt("Developer name", Some("defauil -> erique.dev")); - let develop_mail = prompt("Developer email", Some("defauil -> contato@erique.dev")); - let develop_url = prompt("Your website", Some("defauil -> erique.dev")); - - let mut ph = HashMap::new(); - ph.insert("userEntity", user_entity_lower.clone()); - ph.insert("UserEntity", user_entity_pascal.clone()); - ph.insert("tableName", table_name.clone()); - ph.insert("appName", app_name.clone()); - ph.insert("yourDomain", your_domain.clone()); - ph.insert("AppNameClean", app_name_clean.clone()); - ph.insert("description", description.clone()); - ph.insert("developName", develop_name.clone()); - ph.insert("developMail", develop_mail.clone()); - ph.insert("developUrl", develop_url.clone()); - - let dst_root = PathBuf::from(&app_name); - if dst_root.exists() { - eprintln!("there is already a folder named: {} (remove or choose another application name).", dst_root.display()); - std::process::exit(1); - } - - println!("\nGenerating project -> {}", dst_root.display()); - extract_embedded_template(&dst_root)?; - - let re_user_entity = Regex::new(r"\bUserEntity\b").unwrap(); - let re_app_clean_app = Regex::new(r"\bAppNameCleanApplication\b").unwrap(); - let replacements: Vec<(Regex, String)> = vec![ - (re_user_entity, user_entity_pascal.clone()), - (re_app_clean_app, format!("{}Application", app_name_clean)), - ]; - - let domain_parts = domain_to_parts(&your_domain); - remap_your_domain_paths(&dst_root, &domain_parts)?; - - rename_files_by_tokens(&dst_root, &user_entity_pascal, &app_name_clean)?; - - edit_file_contents(&dst_root, &ph, &replacements)?; - - println!("\nCompleted, Project generated in: {}", dst_root.display()); - Ok(()) + .collect::() } fn main() { diff --git a/template/3layer/.gitattributes b/template/3layer/.gitattributes deleted file mode 100644 index 3b41682..0000000 --- a/template/3layer/.gitattributes +++ /dev/null @@ -1,2 +0,0 @@ -/mvnw text eol=lf -*.cmd text eol=crlf diff --git a/template/3layer/.gitignore b/template/3layer/.gitignore deleted file mode 100644 index 667aaef..0000000 --- a/template/3layer/.gitignore +++ /dev/null @@ -1,33 +0,0 @@ -HELP.md -target/ -.mvn/wrapper/maven-wrapper.jar -!**/src/main/**/target/ -!**/src/test/**/target/ - -### STS ### -.apt_generated -.classpath -.factorypath -.project -.settings -.springBeans -.sts4-cache - -### IntelliJ IDEA ### -.idea -*.iws -*.iml -*.ipr - -### NetBeans ### -/nbproject/private/ -/nbbuild/ -/dist/ -/nbdist/ -/.nb-gradle/ -build/ -!**/src/main/**/build/ -!**/src/test/**/build/ - -### VS Code ### -.vscode/ diff --git a/template/3layer/application/pom.xml b/template/3layer/application/pom.xml deleted file mode 100644 index 5bcae98..0000000 --- a/template/3layer/application/pom.xml +++ /dev/null @@ -1,23 +0,0 @@ - - - 4.0.0 - - - {{yourDomain}} - {{appName}} - 1.0.0 - - - application - - - - {{yourDomain}} - core - 1.0.0 - - - \ No newline at end of file diff --git a/template/3layer/application/src/main/java/your/domain/application/ports/repositories/AdminRepository.java b/template/3layer/application/src/main/java/your/domain/application/ports/repositories/AdminRepository.java deleted file mode 100644 index ad29d2e..0000000 --- a/template/3layer/application/src/main/java/your/domain/application/ports/repositories/AdminRepository.java +++ /dev/null @@ -1,11 +0,0 @@ -package {{yourDomain}}.application.ports.repositories; - -import {{yourDomain}}.core.domain.entities.Admin; - -import java.util.Optional; - -public interface AdminRepository { - Optional findByEmail(String email); - Admin save(Admin admin); - Optional findById(Long id); -} diff --git a/template/3layer/application/src/main/java/your/domain/application/ports/repositories/UserEntityRepository.java b/template/3layer/application/src/main/java/your/domain/application/ports/repositories/UserEntityRepository.java deleted file mode 100644 index fac5e17..0000000 --- a/template/3layer/application/src/main/java/your/domain/application/ports/repositories/UserEntityRepository.java +++ /dev/null @@ -1,11 +0,0 @@ -package {{yourDomain}}.application.ports.repositories; - -import {{yourDomain}}.core.domain.entities.{{UserEntity}}; - -import java.util.Optional; - -public interface {{UserEntity}}Repository { - Optional<{{UserEntity}}> findByEmail(String email); - {{UserEntity}} save({{UserEntity}} {{userEntity}}); - Optional<{{UserEntity}}> findById(Long id); -} \ No newline at end of file diff --git a/template/3layer/application/src/main/java/your/domain/application/ports/services/PasswordService.java b/template/3layer/application/src/main/java/your/domain/application/ports/services/PasswordService.java deleted file mode 100644 index a9c6cbd..0000000 --- a/template/3layer/application/src/main/java/your/domain/application/ports/services/PasswordService.java +++ /dev/null @@ -1,6 +0,0 @@ -package {{yourDomain}}.application.ports.services; - -public interface PasswordService { - String encode(String rawPassword); - boolean matches(String rawPassword, String encodedPassword); -} diff --git a/template/3layer/application/src/main/java/your/domain/application/ports/services/TokenService.java b/template/3layer/application/src/main/java/your/domain/application/ports/services/TokenService.java deleted file mode 100644 index 0a02890..0000000 --- a/template/3layer/application/src/main/java/your/domain/application/ports/services/TokenService.java +++ /dev/null @@ -1,8 +0,0 @@ -package {{yourDomain}}.application.ports.services; - -public interface TokenService { - String generateToken(String email, String role); - boolean validateToken(String token); - String extractEmail(String token); - String extractRole(String token); -} diff --git a/template/3layer/application/src/main/java/your/domain/application/usecases/AuthenticateAdminUseCase.java b/template/3layer/application/src/main/java/your/domain/application/usecases/AuthenticateAdminUseCase.java deleted file mode 100644 index 58bdcd4..0000000 --- a/template/3layer/application/src/main/java/your/domain/application/usecases/AuthenticateAdminUseCase.java +++ /dev/null @@ -1,48 +0,0 @@ -package {{yourDomain}}.application.usecases; - -import {{yourDomain}}.application.ports.repositories.AdminRepository; -import {{yourDomain}}.application.ports.services.PasswordService; -import {{yourDomain}}.application.ports.services.TokenService; -import {{yourDomain}}.core.domain.entities.Admin; -import {{yourDomain}}.core.domain.valueobjects.AuthenticationResult; - -import java.util.Optional; - -public class AuthenticateAdminUseCase { - - private final AdminRepository adminRepository; - private final PasswordService passwordService; - private final TokenService tokenService; - - public AuthenticateAdminUseCase(AdminRepository adminRepository, - PasswordService passwordService, - TokenService tokenService) { - this.adminRepository = adminRepository; - this.passwordService = passwordService; - this.tokenService = tokenService; - } - - public Optional execute(String email, String password) { - Optional adminOpt = adminRepository.findByEmail(email); - - if (adminOpt.isEmpty()) { - return Optional.empty(); - } - - Admin admin = adminOpt.get(); - - if (!passwordService.matches(password, admin.getPassword())) { - return Optional.empty(); - } - - String token = tokenService.generateToken(admin.getEmail(), admin.getRole()); - - return Optional.of(new AuthenticationResult( - token, - admin.getRole(), - admin.getEmail(), - admin.getName() - )); - } - -} diff --git a/template/3layer/application/src/main/java/your/domain/application/usecases/AuthenticateUserEntityUseCase.java b/template/3layer/application/src/main/java/your/domain/application/usecases/AuthenticateUserEntityUseCase.java deleted file mode 100644 index b59ebd2..0000000 --- a/template/3layer/application/src/main/java/your/domain/application/usecases/AuthenticateUserEntityUseCase.java +++ /dev/null @@ -1,48 +0,0 @@ -package {{yourDomain}}.application.usecases; - -import {{yourDomain}}.application.ports.repositories.{{UserEntity}}Repository; -import {{yourDomain}}.application.ports.services.PasswordService; -import {{yourDomain}}.application.ports.services.TokenService; -import {{yourDomain}}.core.domain.entities.{{UserEntity}}; -import {{yourDomain}}.core.domain.valueobjects.AuthenticationResult; - -import java.util.Optional; - -public class Authenticate{{UserEntity}}UseCase { - - private final {{UserEntity}}Repository {{userEntity}}Repository; - private final PasswordService passwordService; - private final TokenService tokenService; - - public Authenticate{{UserEntity}}UseCase({{UserEntity}}Repository {{userEntity}}Repository, - PasswordService passwordService, - TokenService tokenService) { - this.{{userEntity}}Repository = {{userEntity}}Repository; - this.passwordService = passwordService; - this.tokenService = tokenService; - } - - public Optional execute(String email, String password) { - Optional<{{UserEntity}}> {{userEntity}}Opt = {{userEntity}}Repository.findByEmail(email); - - if ({{userEntity}}Opt.isEmpty()) { - return Optional.empty(); - } - - {{UserEntity}} {{userEntity}} = {{userEntity}}Opt.get(); - - if (!passwordService.matches(password, {{userEntity}}.getPassword())) { - return Optional.empty(); - } - - String token = tokenService.generateToken({{userEntity}}.getEmail(), {{userEntity}}.getRole()); - - return Optional.of(new AuthenticationResult( - token, - {{userEntity}}.getRole(), - {{userEntity}}.getEmail(), - {{userEntity}}.getName() - )); - } - -} diff --git a/template/3layer/application/src/main/java/your/domain/application/usecases/CreateUserEntityUseCase.java b/template/3layer/application/src/main/java/your/domain/application/usecases/CreateUserEntityUseCase.java deleted file mode 100644 index 16ab398..0000000 --- a/template/3layer/application/src/main/java/your/domain/application/usecases/CreateUserEntityUseCase.java +++ /dev/null @@ -1,30 +0,0 @@ -package {{yourDomain}}.application.usecases; - -import {{yourDomain}}.application.ports.repositories.{{UserEntity}}Repository; -import {{yourDomain}}.application.ports.services.PasswordService; -import {{yourDomain}}.core.domain.entities.UserEntity; - -import java.util.Optional; - -public class Create{{UserEntity}}UseCase { - private final {{UserEntity}}Repository {{userEntity}}Repository; - private final PasswordService passwordService; - - public Create{{UserEntity}}UseCase({{UserEntity}}Repository {{userEntity}}Repository, PasswordService passwordService) { - this.{{userEntity}}Repository = {{userEntity}}Repository; - this.passwordService = passwordService; - } - - public Optional<{{UserEntity}}> execute(String email, String password, String name) { - Optional<{{UserEntity}}> existing{{UserEntity}} = {{userEntity}}Repository.findByEmail(email); - if (existing{{UserEntity}}.isPresent()) { - return Optional.empty(); - } - - String encodedPassword = passwordService.encode(password); - {{UserEntity}} new{{UserEntity}} = new {{UserEntity}}(email, encodedPassword, name); - {{UserEntity}} saved{{UserEntity}} = {{userEntity}}Repository.save(new{{UserEntity}}); - - return Optional.of(saved{{UserEntity}}); - } -} diff --git a/template/3layer/application/src/main/resources/application.properties b/template/3layer/application/src/main/resources/application.properties deleted file mode 100644 index de94ecd..0000000 --- a/template/3layer/application/src/main/resources/application.properties +++ /dev/null @@ -1 +0,0 @@ -spring.application.name={{appName}} diff --git a/template/3layer/application/src/test/java/dev/falae/api/ApplicationTests.java b/template/3layer/application/src/test/java/dev/falae/api/ApplicationTests.java deleted file mode 100644 index 4ad82ff..0000000 --- a/template/3layer/application/src/test/java/dev/falae/api/ApplicationTests.java +++ /dev/null @@ -1,13 +0,0 @@ -package dev.falae.api; - -import org.junit.jupiter.api.Test; -import org.springframework.boot.test.context.SpringBootTest; - -@SpringBootTest -class ApplicationTests { - - @Test - void contextLoads() { - } - -} diff --git a/template/3layer/core/pom.xml b/template/3layer/core/pom.xml deleted file mode 100644 index 7b78be2..0000000 --- a/template/3layer/core/pom.xml +++ /dev/null @@ -1,18 +0,0 @@ - - - 4.0.0 - - - {{yourDomain}} - {{appName}} - 1.0.0 - - - core - - - - \ No newline at end of file diff --git a/template/3layer/core/src/main/java/your/domain/core/domain/entities/Admin.java b/template/3layer/core/src/main/java/your/domain/core/domain/entities/Admin.java deleted file mode 100644 index 688085d..0000000 --- a/template/3layer/core/src/main/java/your/domain/core/domain/entities/Admin.java +++ /dev/null @@ -1,98 +0,0 @@ -package {{yourDomain}}.core.domain.entities; - -import java.time.LocalDateTime; -import java.util.Objects; - -public class Admin { - private Long id; - private String email; - private String password; - private String name; - private LocalDateTime createdAt; - private LocalDateTime updatedAt; - - public Admin() {} - - public Admin(String email, String password, String name) { - this.email = email; - this.password = password; - this.name = name; - this.createdAt = LocalDateTime.now(); - this.updatedAt = LocalDateTime.now(); - } - - public Admin(Long id, String email, String password, String name, LocalDateTime createdAt, LocalDateTime updatedAt) { - this.id = id; - this.email = email; - this.password = password; - this.name = name; - this.createdAt = createdAt; - this.updatedAt = updatedAt; - } - - public String getRole() { - return "ADMIN"; - } - - // Getters and Setters - public Long getId() { - return id; - } - - public void setId(Long id) { - this.id = id; - } - - public String getEmail() { - return email; - } - - public void setEmail(String email) { - this.email = email; - } - - public String getPassword() { - return password; - } - - public void setPassword(String password) { - this.password = password; - } - - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } - - public LocalDateTime getCreatedAt() { - return createdAt; - } - - public void setCreatedAt(LocalDateTime createdAt) { - this.createdAt = createdAt; - } - - public LocalDateTime getUpdatedAt() { - return updatedAt; - } - - public void setUpdatedAt(LocalDateTime updatedAt) { - this.updatedAt = updatedAt; - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - Admin admin = (Admin) o; - return Objects.equals(id, admin.id) && Objects.equals(email, admin.email); - } - - @Override - public int hashCode() { - return Objects.hash(id, email); - } -} diff --git a/template/3layer/core/src/main/java/your/domain/core/domain/entities/UserEntity.java b/template/3layer/core/src/main/java/your/domain/core/domain/entities/UserEntity.java deleted file mode 100644 index bceec5a..0000000 --- a/template/3layer/core/src/main/java/your/domain/core/domain/entities/UserEntity.java +++ /dev/null @@ -1,98 +0,0 @@ -package {{yourDomain}}.core.domain.entities; - -import java.time.LocalDateTime; -import java.util.Objects; - -public class {{UserEntity}} { - private Long id; - private String email; - private String password; - private String name; - private LocalDateTime createdAt; - private LocalDateTime updatedAt; - - public {{UserEntity}}() {} - - public {{UserEntity}}(String email, String password, String name) { - this.email = email; - this.password = password; - this.name = name; - this.createdAt = LocalDateTime.now(); - this.updatedAt = LocalDateTime.now(); - } - - public {{UserEntity}}(Long id, String email, String password, String name, LocalDateTime createdAt, LocalDateTime updatedAt) { - this.id = id; - this.email = email; - this.password = password; - this.name = name; - this.createdAt = createdAt; - this.updatedAt = updatedAt; - } - - public String getRole() { - return "AUTHOR"; - } - - // Getters and Setters - public Long getId() { - return id; - } - - public void setId(Long id) { - this.id = id; - } - - public String getEmail() { - return email; - } - - public void setEmail(String email) { - this.email = email; - } - - public String getPassword() { - return password; - } - - public void setPassword(String password) { - this.password = password; - } - - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } - - public LocalDateTime getCreatedAt() { - return createdAt; - } - - public void setCreatedAt(LocalDateTime createdAt) { - this.createdAt = createdAt; - } - - public LocalDateTime getUpdatedAt() { - return updatedAt; - } - - public void setUpdatedAt(LocalDateTime updatedAt) { - this.updatedAt = updatedAt; - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - {{UserEntity}} {{UserEntity}} = ({{UserEntity}}) o; - return Objects.equals(id, {{UserEntity}}.id) && Objects.equals(email, {{UserEntity}}.email); - } - - @Override - public int hashCode() { - return Objects.hash(id, email); - } -} diff --git a/template/3layer/core/src/main/java/your/domain/core/domain/valueobjects/AuthenticationResult.java b/template/3layer/core/src/main/java/your/domain/core/domain/valueobjects/AuthenticationResult.java deleted file mode 100644 index 496b8de..0000000 --- a/template/3layer/core/src/main/java/your/domain/core/domain/valueobjects/AuthenticationResult.java +++ /dev/null @@ -1,31 +0,0 @@ -package {{yourDomain}}.core.domain.valueobjects; - -public class AuthenticationResult { - private final String token; - private final String role; - private final String email; - private final String name; - - public AuthenticationResult(String token, String role, String email, String name) { - this.token = token; - this.role = role; - this.email = email; - this.name = name; - } - - public String getToken() { - return token; - } - - public String getRole() { - return role; - } - - public String getEmail() { - return email; - } - - public String getName() { - return name; - } -} diff --git a/template/3layer/core/src/main/resources/application.properties b/template/3layer/core/src/main/resources/application.properties deleted file mode 100644 index de94ecd..0000000 --- a/template/3layer/core/src/main/resources/application.properties +++ /dev/null @@ -1 +0,0 @@ -spring.application.name={{appName}} diff --git a/template/3layer/core/src/test/java/dev/falae/api/ApplicationTests.java b/template/3layer/core/src/test/java/dev/falae/api/ApplicationTests.java deleted file mode 100644 index 4ad82ff..0000000 --- a/template/3layer/core/src/test/java/dev/falae/api/ApplicationTests.java +++ /dev/null @@ -1,13 +0,0 @@ -package dev.falae.api; - -import org.junit.jupiter.api.Test; -import org.springframework.boot.test.context.SpringBootTest; - -@SpringBootTest -class ApplicationTests { - - @Test - void contextLoads() { - } - -} diff --git a/template/3layer/infrastructure/pom.xml b/template/3layer/infrastructure/pom.xml deleted file mode 100644 index 6dcdc61..0000000 --- a/template/3layer/infrastructure/pom.xml +++ /dev/null @@ -1,89 +0,0 @@ - - - 4.0.0 - - - {{yourDomain}} - {{appName}} - 1.0.0 - - - infrastructure - - - - {{yourDomain}} - application - 1.0.0 - - - {{yourDomain}} - core - 1.0.0 - - - org.springframework.boot - spring-boot-starter-web - - - org.springframework.boot - spring-boot-starter-data-jpa - - - org.springframework.boot - spring-boot-starter-security - - - - org.springdoc - springdoc-openapi-starter-webmvc-ui - 2.8.13 - - - org.apache.commons - commons-lang3 - 3.18.0 - - - - org.postgresql - postgresql - runtime - - - - org.flywaydb - flyway-core - 11.13.2 - - - - org.flywaydb - flyway-database-postgresql - 11.13.2 - runtime - - - - - io.jsonwebtoken - jjwt-api - 0.11.5 - - - io.jsonwebtoken - jjwt-impl - 0.11.5 - runtime - - - io.jsonwebtoken - jjwt-jackson - 0.11.5 - runtime - - - \ No newline at end of file diff --git a/template/3layer/infrastructure/src/main/java/your/domain/infrastructure/AppNameCleanApplication.java b/template/3layer/infrastructure/src/main/java/your/domain/infrastructure/AppNameCleanApplication.java deleted file mode 100644 index c73b9de..0000000 --- a/template/3layer/infrastructure/src/main/java/your/domain/infrastructure/AppNameCleanApplication.java +++ /dev/null @@ -1,23 +0,0 @@ -package {{yourDomain}}.infrastructure; - -import org.springframework.boot.SpringApplication; -import org.springframework.boot.autoconfigure.SpringBootApplication; -import org.springframework.boot.autoconfigure.domain.EntityScan; -import org.springframework.context.annotation.ComponentScan; -import org.springframework.data.jpa.repository.config.EnableJpaRepositories; - -@SpringBootApplication -@ComponentScan(basePackages = { - "{{yourDomain}}.application", - "{{yourDomain}}.core", - "{{yourDomain}}.infrastructure" -}) -@EnableJpaRepositories(basePackages = "{{yourDomain}}.infrastructure.adapters.repositories.jpa") -@EntityScan(basePackages = "{{yourDomain}}.infrastructure.adapters.repositories.entities") -public class {{AppNameClean}}Application { - - public static void main(String[] args) { - SpringApplication.run({{AppNameClean}}Application.class, args); - } - -} diff --git a/template/3layer/infrastructure/src/main/java/your/domain/infrastructure/adapters/repositories/JpaAdminRepository.java b/template/3layer/infrastructure/src/main/java/your/domain/infrastructure/adapters/repositories/JpaAdminRepository.java deleted file mode 100644 index ccc6bbd..0000000 --- a/template/3layer/infrastructure/src/main/java/your/domain/infrastructure/adapters/repositories/JpaAdminRepository.java +++ /dev/null @@ -1,59 +0,0 @@ -package {{yourDomain}}.infrastructure.adapters.repositories; - -import {{yourDomain}}.application.ports.repositories.AdminRepository; -import {{yourDomain}}.core.domain.entities.Admin; -import {{yourDomain}}.infrastructure.adapters.repositories.entities.AdminEntity; -import {{yourDomain}}.infrastructure.adapters.repositories.jpa.AdminJpaRepository; -import org.springframework.stereotype.Repository; - -import java.util.Optional; - -@Repository -public class JpaAdminRepository implements AdminRepository { - private final AdminJpaRepository jpaRepository; - - public JpaAdminRepository(AdminJpaRepository jpaRepository) { - this.jpaRepository = jpaRepository; - } - - @Override - public Optional findByEmail(String email) { - return jpaRepository.findByEmail(email) - .map(this::toDomain); - } - - @Override - public Admin save(Admin admin) { - AdminEntity entity = toEntity(admin); - AdminEntity savedEntity = jpaRepository.save(entity); - return toDomain(savedEntity); - } - - @Override - public Optional findById(Long id) { - return jpaRepository.findById(id) - .map(this::toDomain); - } - - private Admin toDomain(AdminEntity entity) { - return new Admin( - entity.getId(), - entity.getEmail(), - entity.getPassword(), - entity.getName(), - entity.getCreatedAt(), - entity.getUpdatedAt() - ); - } - - private AdminEntity toEntity(Admin domain) { - AdminEntity entity = new AdminEntity(); - entity.setId(domain.getId()); - entity.setEmail(domain.getEmail()); - entity.setPassword(domain.getPassword()); - entity.setName(domain.getName()); - entity.setCreatedAt(domain.getCreatedAt()); - entity.setUpdatedAt(domain.getUpdatedAt()); - return entity; - } -} diff --git a/template/3layer/infrastructure/src/main/java/your/domain/infrastructure/adapters/repositories/JpaUserEntityRepository.java b/template/3layer/infrastructure/src/main/java/your/domain/infrastructure/adapters/repositories/JpaUserEntityRepository.java deleted file mode 100644 index 710a386..0000000 --- a/template/3layer/infrastructure/src/main/java/your/domain/infrastructure/adapters/repositories/JpaUserEntityRepository.java +++ /dev/null @@ -1,59 +0,0 @@ -package {{yourDomain}}.infrastructure.adapters.repositories; - -import {{yourDomain}}.application.ports.repositories.{{UserEntity}}Repository; -import {{yourDomain}}.core.domain.entities.{{UserEntity}}; -import {{yourDomain}}.infrastructure.adapters.repositories.entities.{{UserEntity}}Entity; -import {{yourDomain}}.infrastructure.adapters.repositories.jpa.{{UserEntity}}JpaRepository; -import org.springframework.stereotype.Repository; - -import java.util.Optional; - -@Repository -public class Jpa{{UserEntity}}Repository implements {{UserEntity}}Repository { - private final {{UserEntity}}JpaRepository jpaRepository; - - public Jpa{{UserEntity}}Repository({{UserEntity}}JpaRepository jpaRepository) { - this.jpaRepository = jpaRepository; - } - - @Override - public Optional<{{UserEntity}}> findByEmail(String email) { - return jpaRepository.findByEmail(email) - .map(this::toDomain); - } - - @Override - public {{UserEntity}} save({{UserEntity}} {{userEntity}}) { - {{UserEntity}}Entity entity = toEntity({{userEntity}}); - {{UserEntity}}Entity savedEntity = jpaRepository.save(entity); - return toDomain(savedEntity); - } - - @Override - public Optional<{{UserEntity}}> findById(Long id) { - return jpaRepository.findById(id) - .map(this::toDomain); - } - - private {{UserEntity}} toDomain({{UserEntity}}Entity entity) { - return new {{UserEntity}}( - entity.getId(), - entity.getEmail(), - entity.getPassword(), - entity.getName(), - entity.getCreatedAt(), - entity.getUpdatedAt() - ); - } - - private {{UserEntity}}Entity toEntity({{UserEntity}} domain) { - {{UserEntity}}Entity entity = new {{UserEntity}}Entity(); - entity.setId(domain.getId()); - entity.setEmail(domain.getEmail()); - entity.setPassword(domain.getPassword()); - entity.setName(domain.getName()); - entity.setCreatedAt(domain.getCreatedAt()); - entity.setUpdatedAt(domain.getUpdatedAt()); - return entity; - } -} \ No newline at end of file diff --git a/template/3layer/infrastructure/src/main/java/your/domain/infrastructure/adapters/repositories/entities/AdminEntity.java b/template/3layer/infrastructure/src/main/java/your/domain/infrastructure/adapters/repositories/entities/AdminEntity.java deleted file mode 100644 index a491435..0000000 --- a/template/3layer/infrastructure/src/main/java/your/domain/infrastructure/adapters/repositories/entities/AdminEntity.java +++ /dev/null @@ -1,88 +0,0 @@ -package {{yourDomain}}.infrastructure.adapters.repositories.entities; - -import jakarta.persistence.*; - -import java.time.LocalDateTime; - -@Entity -@Table(name = "admins") -public class AdminEntity { - @Id - @GeneratedValue(strategy = GenerationType.IDENTITY) - private Long id; - - @Column(unique = true, nullable = false) - private String email; - - @Column(nullable = false) - private String password; - - @Column(nullable = false) - private String name; - - @Column(name = "created_at") - private LocalDateTime createdAt; - - @Column(name = "updated_at") - private LocalDateTime updatedAt; - - @PrePersist - protected void onCreate() { - createdAt = LocalDateTime.now(); - updatedAt = LocalDateTime.now(); - } - - @PreUpdate - protected void onUpdate() { - updatedAt = LocalDateTime.now(); - } - - // Getters and Setters - public Long getId() { - return id; - } - - public void setId(Long id) { - this.id = id; - } - - public String getEmail() { - return email; - } - - public void setEmail(String email) { - this.email = email; - } - - public String getPassword() { - return password; - } - - public void setPassword(String password) { - this.password = password; - } - - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } - - public LocalDateTime getCreatedAt() { - return createdAt; - } - - public void setCreatedAt(LocalDateTime createdAt) { - this.createdAt = createdAt; - } - - public LocalDateTime getUpdatedAt() { - return updatedAt; - } - - public void setUpdatedAt(LocalDateTime updatedAt) { - this.updatedAt = updatedAt; - } -} diff --git a/template/3layer/infrastructure/src/main/java/your/domain/infrastructure/adapters/repositories/entities/UserEntityEntity.java b/template/3layer/infrastructure/src/main/java/your/domain/infrastructure/adapters/repositories/entities/UserEntityEntity.java deleted file mode 100644 index 36b7017..0000000 --- a/template/3layer/infrastructure/src/main/java/your/domain/infrastructure/adapters/repositories/entities/UserEntityEntity.java +++ /dev/null @@ -1,88 +0,0 @@ -package {{yourDomain}}.infrastructure.adapters.repositories.entities; - -import jakarta.persistence.*; - -import java.time.LocalDateTime; - -@Entity -@Table(name = "{{tableName}}") -public class {{UserEntity}}Entity { - @Id - @GeneratedValue(strategy = GenerationType.IDENTITY) - private Long id; - - @Column(unique = true, nullable = false) - private String email; - - @Column(nullable = false) - private String password; - - @Column(nullable = false) - private String name; - - @Column(name = "created_at") - private LocalDateTime createdAt; - - @Column(name = "updated_at") - private LocalDateTime updatedAt; - - @PrePersist - protected void onCreate() { - createdAt = LocalDateTime.now(); - updatedAt = LocalDateTime.now(); - } - - @PreUpdate - protected void onUpdate() { - updatedAt = LocalDateTime.now(); - } - - // Getters and Setters - public Long getId() { - return id; - } - - public void setId(Long id) { - this.id = id; - } - - public String getEmail() { - return email; - } - - public void setEmail(String email) { - this.email = email; - } - - public String getPassword() { - return password; - } - - public void setPassword(String password) { - this.password = password; - } - - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } - - public LocalDateTime getCreatedAt() { - return createdAt; - } - - public void setCreatedAt(LocalDateTime createdAt) { - this.createdAt = createdAt; - } - - public LocalDateTime getUpdatedAt() { - return updatedAt; - } - - public void setUpdatedAt(LocalDateTime updatedAt) { - this.updatedAt = updatedAt; - } -} diff --git a/template/3layer/infrastructure/src/main/java/your/domain/infrastructure/adapters/repositories/jpa/AdminJpaRepository.java b/template/3layer/infrastructure/src/main/java/your/domain/infrastructure/adapters/repositories/jpa/AdminJpaRepository.java deleted file mode 100644 index 6de9f7e..0000000 --- a/template/3layer/infrastructure/src/main/java/your/domain/infrastructure/adapters/repositories/jpa/AdminJpaRepository.java +++ /dev/null @@ -1,12 +0,0 @@ -package {{yourDomain}}.infrastructure.adapters.repositories.jpa; - -import {{yourDomain}}.infrastructure.adapters.repositories.entities.AdminEntity; -import org.springframework.data.jpa.repository.JpaRepository; -import org.springframework.stereotype.Repository; - -import java.util.Optional; - -@Repository -public interface AdminJpaRepository extends JpaRepository { - Optional findByEmail(String email); -} diff --git a/template/3layer/infrastructure/src/main/java/your/domain/infrastructure/adapters/repositories/jpa/UserEntityJpaRepository.java b/template/3layer/infrastructure/src/main/java/your/domain/infrastructure/adapters/repositories/jpa/UserEntityJpaRepository.java deleted file mode 100644 index b60152a..0000000 --- a/template/3layer/infrastructure/src/main/java/your/domain/infrastructure/adapters/repositories/jpa/UserEntityJpaRepository.java +++ /dev/null @@ -1,12 +0,0 @@ -package {{yourDomain}}.infrastructure.adapters.repositories.jpa; - -import {{yourDomain}}.infrastructure.adapters.repositories.entities.{{UserEntity}}Entity; -import org.springframework.data.jpa.repository.JpaRepository; -import org.springframework.stereotype.Repository; - -import java.util.Optional; - -@Repository -public interface {{UserEntity}}JpaRepository extends JpaRepository<{{UserEntity}}Entity, Long> { - Optional<{{UserEntity}}Entity> findByEmail(String email); -} \ No newline at end of file diff --git a/template/3layer/infrastructure/src/main/java/your/domain/infrastructure/adapters/services/BCryptPasswordService.java b/template/3layer/infrastructure/src/main/java/your/domain/infrastructure/adapters/services/BCryptPasswordService.java deleted file mode 100644 index 9f6402d..0000000 --- a/template/3layer/infrastructure/src/main/java/your/domain/infrastructure/adapters/services/BCryptPasswordService.java +++ /dev/null @@ -1,24 +0,0 @@ -package {{yourDomain}}.infrastructure.adapters.services; - -import {{yourDomain}}.application.ports.services.PasswordService; -import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; -import org.springframework.stereotype.Service; - -@Service -public class BCryptPasswordService implements PasswordService { - private final BCryptPasswordEncoder encoder; - - public BCryptPasswordService() { - this.encoder = new BCryptPasswordEncoder(); - } - - @Override - public String encode(String rawPassword) { - return encoder.encode(rawPassword); - } - - @Override - public boolean matches(String rawPassword, String encodedPassword) { - return encoder.matches(rawPassword, encodedPassword); - } -} diff --git a/template/3layer/infrastructure/src/main/java/your/domain/infrastructure/adapters/services/JwtTokenService.java b/template/3layer/infrastructure/src/main/java/your/domain/infrastructure/adapters/services/JwtTokenService.java deleted file mode 100644 index ad88933..0000000 --- a/template/3layer/infrastructure/src/main/java/your/domain/infrastructure/adapters/services/JwtTokenService.java +++ /dev/null @@ -1,81 +0,0 @@ -package {{yourDomain}}.infrastructure.adapters.services; - -import {{yourDomain}}.application.ports.services.TokenService; -import io.jsonwebtoken.Claims; -import io.jsonwebtoken.Jwts; -import io.jsonwebtoken.SignatureAlgorithm; -import io.jsonwebtoken.security.Keys; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.stereotype.Service; - -import javax.crypto.SecretKey; -import java.util.Date; -import java.util.HashMap; -import java.util.Map; - -@Service -public class JwtTokenService implements TokenService { - - @Value("${jwt.secret:mySecretKey}") - private String secret; - - @Value("${jwt.expiration:86400000}") - private Long expiration; - - private SecretKey getSigningKey() { - return Keys.hmacShaKeyFor(secret.getBytes()); - } - - @Override - public String generateToken(String email, String role) { - Map claims = new HashMap<>(); - claims.put("role", role); - return createToken(claims, email); - } - - private String createToken(Map claims, String subject) { - return Jwts.builder() - .setClaims(claims) - .setSubject(subject) - .setIssuedAt(new Date(System.currentTimeMillis())) - .setExpiration(new Date(System.currentTimeMillis() + expiration)) - .signWith(getSigningKey(), SignatureAlgorithm.HS512) - .compact(); - } - - @Override - public boolean validateToken(String token) { - try { - Jwts.parserBuilder() - .setSigningKey(getSigningKey()) - .build() - .parseClaimsJws(token); - return true; - } catch (Exception e) { - return false; - } - } - - @Override - public String extractEmail(String token) { - return extractClaim(token, Claims::getSubject); - } - - @Override - public String extractRole(String token) { - return extractClaim(token, claims -> claims.get("role", String.class)); - } - - private T extractClaim(String token, java.util.function.Function claimsResolver) { - final Claims claims = extractAllClaims(token); - return claimsResolver.apply(claims); - } - - private Claims extractAllClaims(String token) { - return Jwts.parserBuilder() - .setSigningKey(getSigningKey()) - .build() - .parseClaimsJws(token) - .getBody(); - } -} diff --git a/template/3layer/infrastructure/src/main/java/your/domain/infrastructure/adapters/web/controllers/AuthController.java b/template/3layer/infrastructure/src/main/java/your/domain/infrastructure/adapters/web/controllers/AuthController.java deleted file mode 100644 index 6e2a92a..0000000 --- a/template/3layer/infrastructure/src/main/java/your/domain/infrastructure/adapters/web/controllers/AuthController.java +++ /dev/null @@ -1,97 +0,0 @@ -package {{yourDomain}}.infrastructure.adapters.web.controllers; - -import {{yourDomain}}.application.usecases.AuthenticateAdminUseCase; -import {{yourDomain}}.application.usecases.Authenticate{{UserEntity}}UseCase; -import {{yourDomain}}.core.domain.valueobjects.AuthenticationResult; -import {{yourDomain}}.infrastructure.adapters.web.dto.LoginRequest; -import {{yourDomain}}.infrastructure.adapters.web.dto.LoginResponse; -import jakarta.servlet.http.Cookie; -import jakarta.servlet.http.HttpServletResponse; -import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.RequestBody; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RestController; - -import java.util.Optional; - -@RestController -@RequestMapping("/auth") -public class AuthController { - private final Authenticate{{UserEntity}}UseCase authenticate{{UserEntity}}UseCase; - private final AuthenticateAdminUseCase authenticateAdminUseCase; - - public AuthController(Authenticate{{UserEntity}}UseCase authenticate{{UserEntity}}UseCase, - AuthenticateAdminUseCase authenticateAdminUseCase) { - this.authenticate{{UserEntity}}UseCase = authenticate{{UserEntity}}UseCase; - this.authenticateAdminUseCase = authenticateAdminUseCase; - } - - @PostMapping("/{{userEntity}}/login") - public ResponseEntity login{{UserEntity}}(@RequestBody LoginRequest request, - HttpServletResponse response) { - Optional result = authenticate{{UserEntity}}UseCase.execute( - request.getEmail(), - request.getPassword() - ); - - if (result.isEmpty()) { - return ResponseEntity.badRequest() - .body(new LoginResponse("Invalid credentials", null, null, null)); - } - - AuthenticationResult authResult = result.get(); - setTokenCookie(response, authResult.getToken()); - - return ResponseEntity.ok(new LoginResponse( - "Login successful", - authResult.getRole(), - authResult.getEmail(), - authResult.getName() - )); - } - - @PostMapping("/admin/login") - public ResponseEntity loginAdmin(@RequestBody LoginRequest request, - HttpServletResponse response) { - Optional result = authenticateAdminUseCase.execute( - request.getEmail(), - request.getPassword() - ); - - if (result.isEmpty()) { - return ResponseEntity.badRequest() - .body(new LoginResponse("Invalid credentials", null, null, null)); - } - - AuthenticationResult authResult = result.get(); - setTokenCookie(response, authResult.getToken()); - - return ResponseEntity.ok(new LoginResponse( - "Login successful", - authResult.getRole(), - authResult.getEmail(), - authResult.getName() - )); - } - - @PostMapping("/logout") - public ResponseEntity logout(HttpServletResponse response) { - Cookie cookie = new Cookie("token", null); - cookie.setMaxAge(0); - cookie.setHttpOnly(true); - cookie.setPath("/"); - response.addCookie(cookie); - - return ResponseEntity.ok("Logout successful"); - } - - private void setTokenCookie(HttpServletResponse response, String token) { - Cookie cookie = new Cookie("token", token); - cookie.setHttpOnly(true); - cookie.setSecure(false); // Set to true in production with HTTPS - cookie.setPath("/"); - cookie.setMaxAge(24 * 60 * 60); // 24 hours - response.addCookie(cookie); - } -} diff --git a/template/3layer/infrastructure/src/main/java/your/domain/infrastructure/adapters/web/controllers/TestController.java b/template/3layer/infrastructure/src/main/java/your/domain/infrastructure/adapters/web/controllers/TestController.java deleted file mode 100644 index 21be61b..0000000 --- a/template/3layer/infrastructure/src/main/java/your/domain/infrastructure/adapters/web/controllers/TestController.java +++ /dev/null @@ -1,26 +0,0 @@ -package {{yourDomain}}.infrastructure.adapters.web.controllers; - -import org.springframework.security.core.Authentication; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RestController; - -@RestController -@RequestMapping("/api") -public class TestController { - - @GetMapping("/admin/dashboard") - public String adminDashboard(Authentication authentication) { - return "Welcome to Admin Dashboard, " + authentication.getName() + "!"; - } - - @GetMapping("/{{userEntity}}/profile") - public String authorProfile(Authentication authentication) { - return "Welcome to Author Profile, " + authentication.getName() + "!"; - } - - @GetMapping("/public") - public String publicEndpoint() { - return "This is a public endpoint!"; - } -} diff --git a/template/3layer/infrastructure/src/main/java/your/domain/infrastructure/adapters/web/controllers/UserEntityController.java b/template/3layer/infrastructure/src/main/java/your/domain/infrastructure/adapters/web/controllers/UserEntityController.java deleted file mode 100644 index efe6a2a..0000000 --- a/template/3layer/infrastructure/src/main/java/your/domain/infrastructure/adapters/web/controllers/UserEntityController.java +++ /dev/null @@ -1,53 +0,0 @@ -package {{yourDomain}}.infrastructure.adapters.web.controllers; - -import {{yourDomain}}.application.usecases.Create{{UserEntity}}UseCase; -import {{yourDomain}}.core.domain.entities.{{UserEntity}}; -import {{yourDomain}}.infrastructure.adapters.web.dto.Create{{UserEntity}}Request; -import {{yourDomain}}.infrastructure.adapters.web.dto.Create{{UserEntity}}Response; -import jakarta.validation.Valid; -import org.springframework.http.HttpStatus; -import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.RequestBody; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RestController; - -import java.util.Optional; - -@RestController -@RequestMapping("/api/{{userEntity}}") -public class {{UserEntity}}Controller { - private final Create{{UserEntity}}UseCase create{{UserEntity}}UseCase; - - public {{UserEntity}}Controller(Create{{UserEntity}}UseCase create{{UserEntity}}UseCase) { - this.create{{UserEntity}}UseCase = create{{UserEntity}}UseCase; - } - - @PostMapping - public ResponseEntity create{{UserEntity}}(@Valid @RequestBody Create{{UserEntity}}Request request) { - Optional<{{UserEntity}}> result = create{{UserEntity}}UseCase.execute( - request.getEmail(), - request.getPassword(), - request.getName() - ); - - if (result.isEmpty()) { - return ResponseEntity.status(HttpStatus.CONFLICT) - .body(new Create{{UserEntity}}Response( - "Email already exists", - null, - null, - null - )); - } - - {{UserEntity}} created{{UserEntity}} = result.get(); - return ResponseEntity.status(HttpStatus.CREATED) - .body(new Create{{UserEntity}}Response( - "{{UserEntity}} created successfully", - created{{UserEntity}}.getId(), - created{{UserEntity}}.getEmail(), - created{{UserEntity}}.getName() - )); - } -} diff --git a/template/3layer/infrastructure/src/main/java/your/domain/infrastructure/adapters/web/dto/CreateUserEntityRequest.java b/template/3layer/infrastructure/src/main/java/your/domain/infrastructure/adapters/web/dto/CreateUserEntityRequest.java deleted file mode 100644 index cac0b09..0000000 --- a/template/3layer/infrastructure/src/main/java/your/domain/infrastructure/adapters/web/dto/CreateUserEntityRequest.java +++ /dev/null @@ -1,52 +0,0 @@ -package {{yourDomain}}.infrastructure.adapters.web.dto; - -import jakarta.validation.constraints.Email; -import jakarta.validation.constraints.NotBlank; -import jakarta.validation.constraints.Size; - -public class Create{{UserEntity}}Request { - - @NotBlank(message = "Email is required") - @Email(message = "Email should be valid") - private String email; - - @NotBlank(message = "Password is required") - @Size(min = 6, message = "Password must be at least 6 characters") - private String password; - - @NotBlank(message = "Name is required") - @Size(min = 2, max = 100, message = "Name must be between 2 and 100 characters") - private String name; - - public Create{{UserEntity}}Request() {} - - public Create{{UserEntity}}Request(String email, String password, String name) { - this.email = email; - this.password = password; - this.name = name; - } - - public String getEmail() { - return email; - } - - public void setEmail(String email) { - this.email = email; - } - - public String getPassword() { - return password; - } - - public void setPassword(String password) { - this.password = password; - } - - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } -} diff --git a/template/3layer/infrastructure/src/main/java/your/domain/infrastructure/adapters/web/dto/CreateUserEntityResponse.java b/template/3layer/infrastructure/src/main/java/your/domain/infrastructure/adapters/web/dto/CreateUserEntityResponse.java deleted file mode 100644 index 88d0db4..0000000 --- a/template/3layer/infrastructure/src/main/java/your/domain/infrastructure/adapters/web/dto/CreateUserEntityResponse.java +++ /dev/null @@ -1,49 +0,0 @@ -package {{yourDomain}}.infrastructure.adapters.web.dto; - -public class Create{{UserEntity}}Response { - private String message; - private Long id; - private String email; - private String name; - - public Create{{UserEntity}}Response() {} - - public Create{{UserEntity}}Response(String message, Long id, String email, String name) { - this.message = message; - this.id = id; - this.email = email; - this.name = name; - } - - public String getMessage() { - return message; - } - - public void setMessage(String message) { - this.message = message; - } - - public Long getId() { - return id; - } - - public void setId(Long id) { - this.id = id; - } - - public String getEmail() { - return email; - } - - public void setEmail(String email) { - this.email = email; - } - - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } -} diff --git a/template/3layer/infrastructure/src/main/java/your/domain/infrastructure/adapters/web/dto/LoginRequest.java b/template/3layer/infrastructure/src/main/java/your/domain/infrastructure/adapters/web/dto/LoginRequest.java deleted file mode 100644 index a4254ae..0000000 --- a/template/3layer/infrastructure/src/main/java/your/domain/infrastructure/adapters/web/dto/LoginRequest.java +++ /dev/null @@ -1,29 +0,0 @@ -package {{yourDomain}}.infrastructure.adapters.web.dto; - -public class LoginRequest { - private String email; - private String password; - - public LoginRequest() {} - - public LoginRequest(String email, String password) { - this.email = email; - this.password = password; - } - - public String getEmail() { - return email; - } - - public void setEmail(String email) { - this.email = email; - } - - public String getPassword() { - return password; - } - - public void setPassword(String password) { - this.password = password; - } -} diff --git a/template/3layer/infrastructure/src/main/java/your/domain/infrastructure/adapters/web/dto/LoginResponse.java b/template/3layer/infrastructure/src/main/java/your/domain/infrastructure/adapters/web/dto/LoginResponse.java deleted file mode 100644 index 1e576f6..0000000 --- a/template/3layer/infrastructure/src/main/java/your/domain/infrastructure/adapters/web/dto/LoginResponse.java +++ /dev/null @@ -1,49 +0,0 @@ -package {{yourDomain}}.infrastructure.adapters.web.dto; - -public class LoginResponse { - private String message; - private String role; - private String email; - private String name; - - public LoginResponse() {} - - public LoginResponse(String message, String role, String email, String name) { - this.message = message; - this.role = role; - this.email = email; - this.name = name; - } - - public String getMessage() { - return message; - } - - public void setMessage(String message) { - this.message = message; - } - - public String getRole() { - return role; - } - - public void setRole(String role) { - this.role = role; - } - - public String getEmail() { - return email; - } - - public void setEmail(String email) { - this.email = email; - } - - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } -} diff --git a/template/3layer/infrastructure/src/main/java/your/domain/infrastructure/config/BeanConfiguration.java b/template/3layer/infrastructure/src/main/java/your/domain/infrastructure/config/BeanConfiguration.java deleted file mode 100644 index c6982b8..0000000 --- a/template/3layer/infrastructure/src/main/java/your/domain/infrastructure/config/BeanConfiguration.java +++ /dev/null @@ -1,38 +0,0 @@ -package {{yourDomain}}.infrastructure.config; - -import {{yourDomain}}.application.ports.repositories.AdminRepository; -import {{yourDomain}}.application.ports.repositories.{{UserEntity}}Repository; -import {{yourDomain}}.application.ports.services.PasswordService; -import {{yourDomain}}.application.ports.services.TokenService; -import {{yourDomain}}.application.usecases.AuthenticateAdminUseCase; -import {{yourDomain}}.application.usecases.Authenticate{{UserEntity}}UseCase; -import {{yourDomain}}.application.usecases.Create{{UserEntity}}UseCase; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; - -@Configuration -public class BeanConfiguration { - - @Bean - public Authenticate{{UserEntity}}UseCase authenticate{{UserEntity}}UseCase( - {{UserEntity}}Repository authorRepository, - PasswordService passwordService, - TokenService tokenService) { - return new Authenticate{{UserEntity}}UseCase(authorRepository, passwordService, tokenService); - } - - @Bean - public AuthenticateAdminUseCase authenticateAdminUseCase( - AdminRepository adminRepository, - PasswordService passwordService, - TokenService tokenService) { - return new AuthenticateAdminUseCase(adminRepository, passwordService, tokenService); - } - - @Bean - public Create{{UserEntity}}UseCase create{{UserEntity}}UseCase( - {{UserEntity}}Repository authorRepository, - PasswordService passwordService) { - return new Create{{UserEntity}}UseCase(authorRepository, passwordService); - } -} diff --git a/template/3layer/infrastructure/src/main/java/your/domain/infrastructure/config/doc/OpenAPI30Configuration.java b/template/3layer/infrastructure/src/main/java/your/domain/infrastructure/config/doc/OpenAPI30Configuration.java deleted file mode 100644 index cdbfb5d..0000000 --- a/template/3layer/infrastructure/src/main/java/your/domain/infrastructure/config/doc/OpenAPI30Configuration.java +++ /dev/null @@ -1,45 +0,0 @@ -package {{yourDomain}}.infrastructure.config.doc; - -import io.swagger.v3.oas.annotations.OpenAPIDefinition; -import io.swagger.v3.oas.annotations.info.Contact; -import io.swagger.v3.oas.annotations.info.Info; -import io.swagger.v3.oas.annotations.servers.Server; -import io.swagger.v3.oas.models.Components; -import io.swagger.v3.oas.models.OpenAPI; -import io.swagger.v3.oas.models.security.SecurityRequirement; -import io.swagger.v3.oas.models.security.SecurityScheme; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; - -@Configuration -@OpenAPIDefinition( - info = @Info(title = "{{developName}} - {{appName}}", version = "1.0", - contact = @Contact(name = "{{developName}}", email = "{{developMail}}", url = "{{developUrl}}"), - description = "{{description}}"), - servers = {@Server(url = "http://localhost:8080/", description = "Development")}) -public class OpenAPI30Configuration { - /** - * Configure the OpenAPI components. - * - * @return Returns fully configure OpenAPI object - * @see OpenAPI - */ - - @Bean - public OpenAPI customizeOpenAPI() { - final String securitySchemeName = "bearerAuth"; - return new OpenAPI() - .addSecurityItem(new SecurityRequirement() - .addList(securitySchemeName)) - .components(new Components() - .addSecuritySchemes(securitySchemeName, new SecurityScheme() - .name(securitySchemeName) - .type(SecurityScheme.Type.HTTP) - .scheme("bearer") - .description( - "Forneça o token JWT. O token JWT pode ser obtido na requisição de Login") - .bearerFormat("JWT"))); - - } - -} \ No newline at end of file diff --git a/template/3layer/infrastructure/src/main/java/your/domain/infrastructure/config/security/JwtAuthenticationFilter.java b/template/3layer/infrastructure/src/main/java/your/domain/infrastructure/config/security/JwtAuthenticationFilter.java deleted file mode 100644 index 289e632..0000000 --- a/template/3layer/infrastructure/src/main/java/your/domain/infrastructure/config/security/JwtAuthenticationFilter.java +++ /dev/null @@ -1,60 +0,0 @@ -package {{yourDomain}}.infrastructure.config.security; - -import {{yourDomain}}.application.ports.services.TokenService; -import jakarta.servlet.FilterChain; -import jakarta.servlet.ServletException; -import jakarta.servlet.http.Cookie; -import jakarta.servlet.http.HttpServletRequest; -import jakarta.servlet.http.HttpServletResponse; -import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; -import org.springframework.security.core.authority.SimpleGrantedAuthority; -import org.springframework.security.core.context.SecurityContextHolder; -import org.springframework.stereotype.Component; -import org.springframework.web.filter.OncePerRequestFilter; - -import java.io.IOException; -import java.util.Collections; - -@Component -public class JwtAuthenticationFilter extends OncePerRequestFilter { - private final TokenService tokenService; - - public JwtAuthenticationFilter(TokenService tokenService) { - this.tokenService = tokenService; - } - - @Override - protected void doFilterInternal(HttpServletRequest request, - HttpServletResponse response, - FilterChain filterChain) throws ServletException, IOException, IOException { - - String token = extractTokenFromCookies(request); - - if (token != null && tokenService.validateToken(token)) { - String email = tokenService.extractEmail(token); - String role = tokenService.extractRole(token); - - UsernamePasswordAuthenticationToken authentication = - new UsernamePasswordAuthenticationToken( - email, - null, - Collections.singletonList(new SimpleGrantedAuthority("ROLE_" + role)) - ); - - SecurityContextHolder.getContext().setAuthentication(authentication); - } - - filterChain.doFilter(request, response); - } - - private String extractTokenFromCookies(HttpServletRequest request) { - if (request.getCookies() != null) { - for (Cookie cookie : request.getCookies()) { - if ("token".equals(cookie.getName())) { - return cookie.getValue(); - } - } - } - return null; - } -} diff --git a/template/3layer/infrastructure/src/main/java/your/domain/infrastructure/config/security/SecurityConfig.java b/template/3layer/infrastructure/src/main/java/your/domain/infrastructure/config/security/SecurityConfig.java deleted file mode 100644 index 01cbed9..0000000 --- a/template/3layer/infrastructure/src/main/java/your/domain/infrastructure/config/security/SecurityConfig.java +++ /dev/null @@ -1,42 +0,0 @@ -package {{yourDomain}}.infrastructure.config.security; - -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.config.http.SessionCreationPolicy; -import org.springframework.security.web.SecurityFilterChain; -import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; - -@Configuration -@EnableWebSecurity -public class SecurityConfig { - private final JwtAuthenticationFilter jwtAuthenticationFilter; - - public SecurityConfig(JwtAuthenticationFilter jwtAuthenticationFilter) { - this.jwtAuthenticationFilter = jwtAuthenticationFilter; - } - - @Bean - public SecurityFilterChain filterChain(HttpSecurity http, JwtAuthenticationFilter jwtAuthenticationFilter) throws Exception { - http - .csrf(csrf -> csrf.disable()) - .sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS)) - .authorizeHttpRequests(authz -> authz - //Swagger/OpenAPI - .requestMatchers( - "/v3/api-docs/**", - "/swagger-ui.html", - "/swagger-ui/**" - ).permitAll() - .requestMatchers("/auth/**").permitAll() - .requestMatchers("/api/authors/**").permitAll() - .requestMatchers("/api/admin/**").hasRole("ADMIN") - .requestMatchers("/api/author/**").hasAnyRole("AUTHOR", "ADMIN") - .anyRequest().authenticated() - ) - .addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class); - - return http.build(); - } -} diff --git a/template/3layer/infrastructure/src/main/resources/application.properties b/template/3layer/infrastructure/src/main/resources/application.properties deleted file mode 100644 index 7fc7922..0000000 --- a/template/3layer/infrastructure/src/main/resources/application.properties +++ /dev/null @@ -1,25 +0,0 @@ -spring.application.name={{appName}} - -#Datasource -spring.datasource.driverClassName=org.postgresql.Driver -spring.datasource.url=jdbc:postgresql://${DATABASE_HOST:localhost}:${DATABASE_PORT:5432}/${DATABASE_NAME:your_db} -spring.datasource.username=${DATABASE_USER:postgres} -spring.datasource.password=${DATABASE_PASSWORD:3memvhyka93v} - -#JPA / Hibernate -spring.jpa.hibernate.ddl-auto=validate -spring.jpa.show-sql=true -spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.PostgreSQLDialect -spring.jpa.properties.hibernate.format_sql=true - -#JWT -jwt.secret=${TOKEN_SECRET_KEY:zT#xVnu_-EL5E188~'FPv,BNNxMMr!ii#8fN#@%$kZQANBD.fuR6@cNQwZe-VXUe;mT}o2g$y;-FBcB~s<.*m,$/} -jwt.expiration=86400000 - -#Logging -logging.level.com.example=DEBUG -logging.level.org.springframework.security=DEBUG - -#flyway -spring.flyway.locations=classpath:db/migration/postgres -spring.flyway.enabled=true diff --git a/template/3layer/infrastructure/src/main/resources/db/migration/postgres/V01_01__insert_data_test.sql b/template/3layer/infrastructure/src/main/resources/db/migration/postgres/V01_01__insert_data_test.sql deleted file mode 100644 index 4a4132c..0000000 --- a/template/3layer/infrastructure/src/main/resources/db/migration/postgres/V01_01__insert_data_test.sql +++ /dev/null @@ -1,32 +0,0 @@ ---default schema: public -CREATE TABLE IF NOT EXISTS admins ( - id BIGSERIAL PRIMARY KEY, - email VARCHAR(255) NOT NULL UNIQUE, - password VARCHAR(255) NOT NULL, - name VARCHAR(255) NOT NULL, - created_at TIMESTAMP NOT NULL DEFAULT NOW(), - updated_at TIMESTAMP NOT NULL DEFAULT NOW() -); - -CREATE TABLE IF NOT EXISTS {{tableName}} ( - id BIGSERIAL PRIMARY KEY, - email VARCHAR(255) NOT NULL UNIQUE, - password VARCHAR(255) NOT NULL, - name VARCHAR(255) NOT NULL, - created_at TIMESTAMP NOT NULL DEFAULT NOW(), - updated_at TIMESTAMP NOT NULL DEFAULT NOW() -); - -CREATE INDEX IF NOT EXISTS idx_admins_email ON admins (email); -CREATE INDEX IF NOT EXISTS idx_authors_email ON {{tableName}} (email); - --- default password: test123456 -INSERT INTO admins (email, password, name, created_at, updated_at) -VALUES - ('admin1@{{yourDomain}}', '$2a$10$XTcZJSBbcIdA7PD2Ta8fmu4EmZ2tasvrPoHM2BdUtd.mYi2I5EBFK', 'Admin One', NOW(), NOW()), - ('admin2@{{yourDomain}}', '$2a$10$HAtgYRWeNHDeyA4kntuq6OrEMN8Qgz86XN0ftyg.wsBWXzTunAmKe', 'Admin Two', NOW(), NOW()); - -INSERT INTO {{tableName}} (email, password, name, created_at, updated_at) -VALUES - ('{{userEntity}}1@{{yourDomain}}', '$2a$10$xi3eengxM5..Sa16AqgRU.cZ7lltDkacVlXLbYRqrzzttDVprHS06', '{{userEntity}} One', NOW(), NOW()), - ('{{userEntity}}2@{{yourDomain}}', '$2a$10$VgpXD/oN91RpM/OH9s/3OO5B/BGrfpOAcV/0FPRPKu0ZJV1ITuZey', '{{userEntity}} Two', NOW(), NOW()); \ No newline at end of file diff --git a/template/3layer/infrastructure/src/test/java/dev/falae/api/ApplicationTests.java b/template/3layer/infrastructure/src/test/java/dev/falae/api/ApplicationTests.java deleted file mode 100644 index 4ad82ff..0000000 --- a/template/3layer/infrastructure/src/test/java/dev/falae/api/ApplicationTests.java +++ /dev/null @@ -1,13 +0,0 @@ -package dev.falae.api; - -import org.junit.jupiter.api.Test; -import org.springframework.boot.test.context.SpringBootTest; - -@SpringBootTest -class ApplicationTests { - - @Test - void contextLoads() { - } - -} diff --git a/template/3layer/mvnw b/template/3layer/mvnw deleted file mode 100644 index bd8896b..0000000 --- a/template/3layer/mvnw +++ /dev/null @@ -1,295 +0,0 @@ -#!/bin/sh -# ---------------------------------------------------------------------------- -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you under the Apache License, Version 2.0 (the -# "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, -# software distributed under the License is distributed on an -# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -# KIND, either express or implied. See the License for the -# specific language governing permissions and limitations -# under the License. -# ---------------------------------------------------------------------------- - -# ---------------------------------------------------------------------------- -# Apache Maven Wrapper startup batch script, version 3.3.4 -# -# Optional ENV vars -# ----------------- -# JAVA_HOME - location of a JDK home dir, required when download maven via java source -# MVNW_REPOURL - repo url base for downloading maven distribution -# MVNW_USERNAME/MVNW_PASSWORD - user and password for downloading maven -# MVNW_VERBOSE - true: enable verbose log; debug: trace the mvnw script; others: silence the output -# ---------------------------------------------------------------------------- - -set -euf -[ "${MVNW_VERBOSE-}" != debug ] || set -x - -# OS specific support. -native_path() { printf %s\\n "$1"; } -case "$(uname)" in -CYGWIN* | MINGW*) - [ -z "${JAVA_HOME-}" ] || JAVA_HOME="$(cygpath --unix "$JAVA_HOME")" - native_path() { cygpath --path --windows "$1"; } - ;; -esac - -# set JAVACMD and JAVACCMD -set_java_home() { - # For Cygwin and MinGW, ensure paths are in Unix format before anything is touched - if [ -n "${JAVA_HOME-}" ]; then - if [ -x "$JAVA_HOME/jre/sh/java" ]; then - # IBM's JDK on AIX uses strange locations for the executables - JAVACMD="$JAVA_HOME/jre/sh/java" - JAVACCMD="$JAVA_HOME/jre/sh/javac" - else - JAVACMD="$JAVA_HOME/bin/java" - JAVACCMD="$JAVA_HOME/bin/javac" - - if [ ! -x "$JAVACMD" ] || [ ! -x "$JAVACCMD" ]; then - echo "The JAVA_HOME environment variable is not defined correctly, so mvnw cannot run." >&2 - echo "JAVA_HOME is set to \"$JAVA_HOME\", but \"\$JAVA_HOME/bin/java\" or \"\$JAVA_HOME/bin/javac\" does not exist." >&2 - return 1 - fi - fi - else - JAVACMD="$( - 'set' +e - 'unset' -f command 2>/dev/null - 'command' -v java - )" || : - JAVACCMD="$( - 'set' +e - 'unset' -f command 2>/dev/null - 'command' -v javac - )" || : - - if [ ! -x "${JAVACMD-}" ] || [ ! -x "${JAVACCMD-}" ]; then - echo "The java/javac command does not exist in PATH nor is JAVA_HOME set, so mvnw cannot run." >&2 - return 1 - fi - fi -} - -# hash string like Java String::hashCode -hash_string() { - str="${1:-}" h=0 - while [ -n "$str" ]; do - char="${str%"${str#?}"}" - h=$(((h * 31 + $(LC_CTYPE=C printf %d "'$char")) % 4294967296)) - str="${str#?}" - done - printf %x\\n $h -} - -verbose() { :; } -[ "${MVNW_VERBOSE-}" != true ] || verbose() { printf %s\\n "${1-}"; } - -die() { - printf %s\\n "$1" >&2 - exit 1 -} - -trim() { - # MWRAPPER-139: - # Trims trailing and leading whitespace, carriage returns, tabs, and linefeeds. - # Needed for removing poorly interpreted newline sequences when running in more - # exotic environments such as mingw bash on Windows. - printf "%s" "${1}" | tr -d '[:space:]' -} - -scriptDir="$(dirname "$0")" -scriptName="$(basename "$0")" - -# parse distributionUrl and optional distributionSha256Sum, requires .mvn/wrapper/maven-wrapper.properties -while IFS="=" read -r key value; do - case "${key-}" in - distributionUrl) distributionUrl=$(trim "${value-}") ;; - distributionSha256Sum) distributionSha256Sum=$(trim "${value-}") ;; - esac -done <"$scriptDir/.mvn/wrapper/maven-wrapper.properties" -[ -n "${distributionUrl-}" ] || die "cannot read distributionUrl property in $scriptDir/.mvn/wrapper/maven-wrapper.properties" - -case "${distributionUrl##*/}" in -maven-mvnd-*bin.*) - MVN_CMD=mvnd.sh _MVNW_REPO_PATTERN=/maven/mvnd/ - case "${PROCESSOR_ARCHITECTURE-}${PROCESSOR_ARCHITEW6432-}:$(uname -a)" in - *AMD64:CYGWIN* | *AMD64:MINGW*) distributionPlatform=windows-amd64 ;; - :Darwin*x86_64) distributionPlatform=darwin-amd64 ;; - :Darwin*arm64) distributionPlatform=darwin-aarch64 ;; - :Linux*x86_64*) distributionPlatform=linux-amd64 ;; - *) - echo "Cannot detect native platform for mvnd on $(uname)-$(uname -m), use pure java version" >&2 - distributionPlatform=linux-amd64 - ;; - esac - distributionUrl="${distributionUrl%-bin.*}-$distributionPlatform.zip" - ;; -maven-mvnd-*) MVN_CMD=mvnd.sh _MVNW_REPO_PATTERN=/maven/mvnd/ ;; -*) MVN_CMD="mvn${scriptName#mvnw}" _MVNW_REPO_PATTERN=/org/apache/maven/ ;; -esac - -# apply MVNW_REPOURL and calculate MAVEN_HOME -# maven home pattern: ~/.m2/wrapper/dists/{apache-maven-,maven-mvnd--}/ -[ -z "${MVNW_REPOURL-}" ] || distributionUrl="$MVNW_REPOURL$_MVNW_REPO_PATTERN${distributionUrl#*"$_MVNW_REPO_PATTERN"}" -distributionUrlName="${distributionUrl##*/}" -distributionUrlNameMain="${distributionUrlName%.*}" -distributionUrlNameMain="${distributionUrlNameMain%-bin}" -MAVEN_USER_HOME="${MAVEN_USER_HOME:-${HOME}/.m2}" -MAVEN_HOME="${MAVEN_USER_HOME}/wrapper/dists/${distributionUrlNameMain-}/$(hash_string "$distributionUrl")" - -exec_maven() { - unset MVNW_VERBOSE MVNW_USERNAME MVNW_PASSWORD MVNW_REPOURL || : - exec "$MAVEN_HOME/bin/$MVN_CMD" "$@" || die "cannot exec $MAVEN_HOME/bin/$MVN_CMD" -} - -if [ -d "$MAVEN_HOME" ]; then - verbose "found existing MAVEN_HOME at $MAVEN_HOME" - exec_maven "$@" -fi - -case "${distributionUrl-}" in -*?-bin.zip | *?maven-mvnd-?*-?*.zip) ;; -*) die "distributionUrl is not valid, must match *-bin.zip or maven-mvnd-*.zip, but found '${distributionUrl-}'" ;; -esac - -# prepare tmp dir -if TMP_DOWNLOAD_DIR="$(mktemp -d)" && [ -d "$TMP_DOWNLOAD_DIR" ]; then - clean() { rm -rf -- "$TMP_DOWNLOAD_DIR"; } - trap clean HUP INT TERM EXIT -else - die "cannot create temp dir" -fi - -mkdir -p -- "${MAVEN_HOME%/*}" - -# Download and Install Apache Maven -verbose "Couldn't find MAVEN_HOME, downloading and installing it ..." -verbose "Downloading from: $distributionUrl" -verbose "Downloading to: $TMP_DOWNLOAD_DIR/$distributionUrlName" - -# select .zip or .tar.gz -if ! command -v unzip >/dev/null; then - distributionUrl="${distributionUrl%.zip}.tar.gz" - distributionUrlName="${distributionUrl##*/}" -fi - -# verbose opt -__MVNW_QUIET_WGET=--quiet __MVNW_QUIET_CURL=--silent __MVNW_QUIET_UNZIP=-q __MVNW_QUIET_TAR='' -[ "${MVNW_VERBOSE-}" != true ] || __MVNW_QUIET_WGET='' __MVNW_QUIET_CURL='' __MVNW_QUIET_UNZIP='' __MVNW_QUIET_TAR=v - -# normalize http auth -case "${MVNW_PASSWORD:+has-password}" in -'') MVNW_USERNAME='' MVNW_PASSWORD='' ;; -has-password) [ -n "${MVNW_USERNAME-}" ] || MVNW_USERNAME='' MVNW_PASSWORD='' ;; -esac - -if [ -z "${MVNW_USERNAME-}" ] && command -v wget >/dev/null; then - verbose "Found wget ... using wget" - wget ${__MVNW_QUIET_WGET:+"$__MVNW_QUIET_WGET"} "$distributionUrl" -O "$TMP_DOWNLOAD_DIR/$distributionUrlName" || die "wget: Failed to fetch $distributionUrl" -elif [ -z "${MVNW_USERNAME-}" ] && command -v curl >/dev/null; then - verbose "Found curl ... using curl" - curl ${__MVNW_QUIET_CURL:+"$__MVNW_QUIET_CURL"} -f -L -o "$TMP_DOWNLOAD_DIR/$distributionUrlName" "$distributionUrl" || die "curl: Failed to fetch $distributionUrl" -elif set_java_home; then - verbose "Falling back to use Java to download" - javaSource="$TMP_DOWNLOAD_DIR/Downloader.java" - targetZip="$TMP_DOWNLOAD_DIR/$distributionUrlName" - cat >"$javaSource" <<-END - public class Downloader extends java.net.Authenticator - { - protected java.net.PasswordAuthentication getPasswordAuthentication() - { - return new java.net.PasswordAuthentication( System.getenv( "MVNW_USERNAME" ), System.getenv( "MVNW_PASSWORD" ).toCharArray() ); - } - public static void main( String[] args ) throws Exception - { - setDefault( new Downloader() ); - java.nio.file.Files.copy( java.net.URI.create( args[0] ).toURL().openStream(), java.nio.file.Paths.get( args[1] ).toAbsolutePath().normalize() ); - } - } - END - # For Cygwin/MinGW, switch paths to Windows format before running javac and java - verbose " - Compiling Downloader.java ..." - "$(native_path "$JAVACCMD")" "$(native_path "$javaSource")" || die "Failed to compile Downloader.java" - verbose " - Running Downloader.java ..." - "$(native_path "$JAVACMD")" -cp "$(native_path "$TMP_DOWNLOAD_DIR")" Downloader "$distributionUrl" "$(native_path "$targetZip")" -fi - -# If specified, validate the SHA-256 sum of the Maven distribution zip file -if [ -n "${distributionSha256Sum-}" ]; then - distributionSha256Result=false - if [ "$MVN_CMD" = mvnd.sh ]; then - echo "Checksum validation is not supported for maven-mvnd." >&2 - echo "Please disable validation by removing 'distributionSha256Sum' from your maven-wrapper.properties." >&2 - exit 1 - elif command -v sha256sum >/dev/null; then - if echo "$distributionSha256Sum $TMP_DOWNLOAD_DIR/$distributionUrlName" | sha256sum -c - >/dev/null 2>&1; then - distributionSha256Result=true - fi - elif command -v shasum >/dev/null; then - if echo "$distributionSha256Sum $TMP_DOWNLOAD_DIR/$distributionUrlName" | shasum -a 256 -c >/dev/null 2>&1; then - distributionSha256Result=true - fi - else - echo "Checksum validation was requested but neither 'sha256sum' or 'shasum' are available." >&2 - echo "Please install either command, or disable validation by removing 'distributionSha256Sum' from your maven-wrapper.properties." >&2 - exit 1 - fi - if [ $distributionSha256Result = false ]; then - echo "Error: Failed to validate Maven distribution SHA-256, your Maven distribution might be compromised." >&2 - echo "If you updated your Maven version, you need to update the specified distributionSha256Sum property." >&2 - exit 1 - fi -fi - -# unzip and move -if command -v unzip >/dev/null; then - unzip ${__MVNW_QUIET_UNZIP:+"$__MVNW_QUIET_UNZIP"} "$TMP_DOWNLOAD_DIR/$distributionUrlName" -d "$TMP_DOWNLOAD_DIR" || die "failed to unzip" -else - tar xzf${__MVNW_QUIET_TAR:+"$__MVNW_QUIET_TAR"} "$TMP_DOWNLOAD_DIR/$distributionUrlName" -C "$TMP_DOWNLOAD_DIR" || die "failed to untar" -fi - -# Find the actual extracted directory name (handles snapshots where filename != directory name) -actualDistributionDir="" - -# First try the expected directory name (for regular distributions) -if [ -d "$TMP_DOWNLOAD_DIR/$distributionUrlNameMain" ]; then - if [ -f "$TMP_DOWNLOAD_DIR/$distributionUrlNameMain/bin/$MVN_CMD" ]; then - actualDistributionDir="$distributionUrlNameMain" - fi -fi - -# If not found, search for any directory with the Maven executable (for snapshots) -if [ -z "$actualDistributionDir" ]; then - # enable globbing to iterate over items - set +f - for dir in "$TMP_DOWNLOAD_DIR"/*; do - if [ -d "$dir" ]; then - if [ -f "$dir/bin/$MVN_CMD" ]; then - actualDistributionDir="$(basename "$dir")" - break - fi - fi - done - set -f -fi - -if [ -z "$actualDistributionDir" ]; then - verbose "Contents of $TMP_DOWNLOAD_DIR:" - verbose "$(ls -la "$TMP_DOWNLOAD_DIR")" - die "Could not find Maven distribution directory in extracted archive" -fi - -verbose "Found extracted Maven distribution directory: $actualDistributionDir" -printf %s\\n "$distributionUrl" >"$TMP_DOWNLOAD_DIR/$actualDistributionDir/mvnw.url" -mv -- "$TMP_DOWNLOAD_DIR/$actualDistributionDir" "$MAVEN_HOME" || [ -d "$MAVEN_HOME" ] || die "fail to move MAVEN_HOME" - -clean || : -exec_maven "$@" diff --git a/template/3layer/mvnw.cmd b/template/3layer/mvnw.cmd deleted file mode 100644 index 92450f9..0000000 --- a/template/3layer/mvnw.cmd +++ /dev/null @@ -1,189 +0,0 @@ -<# : batch portion -@REM ---------------------------------------------------------------------------- -@REM Licensed to the Apache Software Foundation (ASF) under one -@REM or more contributor license agreements. See the NOTICE file -@REM distributed with this work for additional information -@REM regarding copyright ownership. The ASF licenses this file -@REM to you under the Apache License, Version 2.0 (the -@REM "License"); you may not use this file except in compliance -@REM with the License. You may obtain a copy of the License at -@REM -@REM http://www.apache.org/licenses/LICENSE-2.0 -@REM -@REM Unless required by applicable law or agreed to in writing, -@REM software distributed under the License is distributed on an -@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -@REM KIND, either express or implied. See the License for the -@REM specific language governing permissions and limitations -@REM under the License. -@REM ---------------------------------------------------------------------------- - -@REM ---------------------------------------------------------------------------- -@REM Apache Maven Wrapper startup batch script, version 3.3.4 -@REM -@REM Optional ENV vars -@REM MVNW_REPOURL - repo url base for downloading maven distribution -@REM MVNW_USERNAME/MVNW_PASSWORD - user and password for downloading maven -@REM MVNW_VERBOSE - true: enable verbose log; others: silence the output -@REM ---------------------------------------------------------------------------- - -@IF "%__MVNW_ARG0_NAME__%"=="" (SET __MVNW_ARG0_NAME__=%~nx0) -@SET __MVNW_CMD__= -@SET __MVNW_ERROR__= -@SET __MVNW_PSMODULEP_SAVE=%PSModulePath% -@SET PSModulePath= -@FOR /F "usebackq tokens=1* delims==" %%A IN (`powershell -noprofile "& {$scriptDir='%~dp0'; $script='%__MVNW_ARG0_NAME__%'; icm -ScriptBlock ([Scriptblock]::Create((Get-Content -Raw '%~f0'))) -NoNewScope}"`) DO @( - IF "%%A"=="MVN_CMD" (set __MVNW_CMD__=%%B) ELSE IF "%%B"=="" (echo %%A) ELSE (echo %%A=%%B) -) -@SET PSModulePath=%__MVNW_PSMODULEP_SAVE% -@SET __MVNW_PSMODULEP_SAVE= -@SET __MVNW_ARG0_NAME__= -@SET MVNW_USERNAME= -@SET MVNW_PASSWORD= -@IF NOT "%__MVNW_CMD__%"=="" ("%__MVNW_CMD__%" %*) -@echo Cannot start maven from wrapper >&2 && exit /b 1 -@GOTO :EOF -: end batch / begin powershell #> - -$ErrorActionPreference = "Stop" -if ($env:MVNW_VERBOSE -eq "true") { - $VerbosePreference = "Continue" -} - -# calculate distributionUrl, requires .mvn/wrapper/maven-wrapper.properties -$distributionUrl = (Get-Content -Raw "$scriptDir/.mvn/wrapper/maven-wrapper.properties" | ConvertFrom-StringData).distributionUrl -if (!$distributionUrl) { - Write-Error "cannot read distributionUrl property in $scriptDir/.mvn/wrapper/maven-wrapper.properties" -} - -switch -wildcard -casesensitive ( $($distributionUrl -replace '^.*/','') ) { - "maven-mvnd-*" { - $USE_MVND = $true - $distributionUrl = $distributionUrl -replace '-bin\.[^.]*$',"-windows-amd64.zip" - $MVN_CMD = "mvnd.cmd" - break - } - default { - $USE_MVND = $false - $MVN_CMD = $script -replace '^mvnw','mvn' - break - } -} - -# apply MVNW_REPOURL and calculate MAVEN_HOME -# maven home pattern: ~/.m2/wrapper/dists/{apache-maven-,maven-mvnd--}/ -if ($env:MVNW_REPOURL) { - $MVNW_REPO_PATTERN = if ($USE_MVND -eq $False) { "/org/apache/maven/" } else { "/maven/mvnd/" } - $distributionUrl = "$env:MVNW_REPOURL$MVNW_REPO_PATTERN$($distributionUrl -replace "^.*$MVNW_REPO_PATTERN",'')" -} -$distributionUrlName = $distributionUrl -replace '^.*/','' -$distributionUrlNameMain = $distributionUrlName -replace '\.[^.]*$','' -replace '-bin$','' - -$MAVEN_M2_PATH = "$HOME/.m2" -if ($env:MAVEN_USER_HOME) { - $MAVEN_M2_PATH = "$env:MAVEN_USER_HOME" -} - -if (-not (Test-Path -Path $MAVEN_M2_PATH)) { - New-Item -Path $MAVEN_M2_PATH -ItemType Directory | Out-Null -} - -$MAVEN_WRAPPER_DISTS = $null -if ((Get-Item $MAVEN_M2_PATH).Target[0] -eq $null) { - $MAVEN_WRAPPER_DISTS = "$MAVEN_M2_PATH/wrapper/dists" -} else { - $MAVEN_WRAPPER_DISTS = (Get-Item $MAVEN_M2_PATH).Target[0] + "/wrapper/dists" -} - -$MAVEN_HOME_PARENT = "$MAVEN_WRAPPER_DISTS/$distributionUrlNameMain" -$MAVEN_HOME_NAME = ([System.Security.Cryptography.SHA256]::Create().ComputeHash([byte[]][char[]]$distributionUrl) | ForEach-Object {$_.ToString("x2")}) -join '' -$MAVEN_HOME = "$MAVEN_HOME_PARENT/$MAVEN_HOME_NAME" - -if (Test-Path -Path "$MAVEN_HOME" -PathType Container) { - Write-Verbose "found existing MAVEN_HOME at $MAVEN_HOME" - Write-Output "MVN_CMD=$MAVEN_HOME/bin/$MVN_CMD" - exit $? -} - -if (! $distributionUrlNameMain -or ($distributionUrlName -eq $distributionUrlNameMain)) { - Write-Error "distributionUrl is not valid, must end with *-bin.zip, but found $distributionUrl" -} - -# prepare tmp dir -$TMP_DOWNLOAD_DIR_HOLDER = New-TemporaryFile -$TMP_DOWNLOAD_DIR = New-Item -Itemtype Directory -Path "$TMP_DOWNLOAD_DIR_HOLDER.dir" -$TMP_DOWNLOAD_DIR_HOLDER.Delete() | Out-Null -trap { - if ($TMP_DOWNLOAD_DIR.Exists) { - try { Remove-Item $TMP_DOWNLOAD_DIR -Recurse -Force | Out-Null } - catch { Write-Warning "Cannot remove $TMP_DOWNLOAD_DIR" } - } -} - -New-Item -Itemtype Directory -Path "$MAVEN_HOME_PARENT" -Force | Out-Null - -# Download and Install Apache Maven -Write-Verbose "Couldn't find MAVEN_HOME, downloading and installing it ..." -Write-Verbose "Downloading from: $distributionUrl" -Write-Verbose "Downloading to: $TMP_DOWNLOAD_DIR/$distributionUrlName" - -$webclient = New-Object System.Net.WebClient -if ($env:MVNW_USERNAME -and $env:MVNW_PASSWORD) { - $webclient.Credentials = New-Object System.Net.NetworkCredential($env:MVNW_USERNAME, $env:MVNW_PASSWORD) -} -[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 -$webclient.DownloadFile($distributionUrl, "$TMP_DOWNLOAD_DIR/$distributionUrlName") | Out-Null - -# If specified, validate the SHA-256 sum of the Maven distribution zip file -$distributionSha256Sum = (Get-Content -Raw "$scriptDir/.mvn/wrapper/maven-wrapper.properties" | ConvertFrom-StringData).distributionSha256Sum -if ($distributionSha256Sum) { - if ($USE_MVND) { - Write-Error "Checksum validation is not supported for maven-mvnd. `nPlease disable validation by removing 'distributionSha256Sum' from your maven-wrapper.properties." - } - Import-Module $PSHOME\Modules\Microsoft.PowerShell.Utility -Function Get-FileHash - if ((Get-FileHash "$TMP_DOWNLOAD_DIR/$distributionUrlName" -Algorithm SHA256).Hash.ToLower() -ne $distributionSha256Sum) { - Write-Error "Error: Failed to validate Maven distribution SHA-256, your Maven distribution might be compromised. If you updated your Maven version, you need to update the specified distributionSha256Sum property." - } -} - -# unzip and move -Expand-Archive "$TMP_DOWNLOAD_DIR/$distributionUrlName" -DestinationPath "$TMP_DOWNLOAD_DIR" | Out-Null - -# Find the actual extracted directory name (handles snapshots where filename != directory name) -$actualDistributionDir = "" - -# First try the expected directory name (for regular distributions) -$expectedPath = Join-Path "$TMP_DOWNLOAD_DIR" "$distributionUrlNameMain" -$expectedMvnPath = Join-Path "$expectedPath" "bin/$MVN_CMD" -if ((Test-Path -Path $expectedPath -PathType Container) -and (Test-Path -Path $expectedMvnPath -PathType Leaf)) { - $actualDistributionDir = $distributionUrlNameMain -} - -# If not found, search for any directory with the Maven executable (for snapshots) -if (!$actualDistributionDir) { - Get-ChildItem -Path "$TMP_DOWNLOAD_DIR" -Directory | ForEach-Object { - $testPath = Join-Path $_.FullName "bin/$MVN_CMD" - if (Test-Path -Path $testPath -PathType Leaf) { - $actualDistributionDir = $_.Name - } - } -} - -if (!$actualDistributionDir) { - Write-Error "Could not find Maven distribution directory in extracted archive" -} - -Write-Verbose "Found extracted Maven distribution directory: $actualDistributionDir" -Rename-Item -Path "$TMP_DOWNLOAD_DIR/$actualDistributionDir" -NewName $MAVEN_HOME_NAME | Out-Null -try { - Move-Item -Path "$TMP_DOWNLOAD_DIR/$MAVEN_HOME_NAME" -Destination $MAVEN_HOME_PARENT | Out-Null -} catch { - if (! (Test-Path -Path "$MAVEN_HOME" -PathType Container)) { - Write-Error "fail to move MAVEN_HOME" - } -} finally { - try { Remove-Item $TMP_DOWNLOAD_DIR -Recurse -Force | Out-Null } - catch { Write-Warning "Cannot remove $TMP_DOWNLOAD_DIR" } -} - -Write-Output "MVN_CMD=$MAVEN_HOME/bin/$MVN_CMD" diff --git a/template/3layer/pom.xml b/template/3layer/pom.xml deleted file mode 100644 index 78e531c..0000000 --- a/template/3layer/pom.xml +++ /dev/null @@ -1,36 +0,0 @@ - - - 4.0.0 - - {{yourDomain}} - {{appName}} - 1.0.0 - pom - - - org.springframework.boot - spring-boot-starter-parent - 3.5.6 - - - - - 25 - 25 - UTF-8 - - - - core - application - infrastructure - - - - - - - \ No newline at end of file From d7464e0809dbeae29c61a8b993266ccce9ab0fce Mon Sep 17 00:00:00 2001 From: "erique.rocha" Date: Wed, 5 Nov 2025 17:04:38 -0300 Subject: [PATCH 2/3] feat: generate JSON with the project structure --- Cargo.lock | 63 ++++++++++++++++++++++++++++++++++++ Cargo.toml | 2 ++ src/main.rs | 92 ++++++++++++++++++++++++++++++++++++++++++----------- 3 files changed, 139 insertions(+), 18 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 4547c6c..8a430c3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -103,6 +103,8 @@ name = "developerStartSpringboot" version = "0.1.0" dependencies = [ "clap", + "serde", + "serde_json", ] [[package]] @@ -117,6 +119,18 @@ version = "1.70.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" +[[package]] +name = "itoa" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" + +[[package]] +name = "memchr" +version = "2.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273" + [[package]] name = "once_cell_polyfill" version = "1.70.1" @@ -141,6 +155,55 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "ryu" +version = "1.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" + +[[package]] +name = "serde" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" +dependencies = [ + "serde_core", + "serde_derive", +] + +[[package]] +name = "serde_core" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.145" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "402a6f66d8c709116cf22f558eab210f5a50187f702eb4d7e5ef38d9a7f1c79c" +dependencies = [ + "itoa", + "memchr", + "ryu", + "serde", + "serde_core", +] + [[package]] name = "strsim" version = "0.11.1" diff --git a/Cargo.toml b/Cargo.toml index 8ba71f8..08e66a3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,6 +18,8 @@ codegen-units = 1 [dependencies] clap = { version = "4.5.48", features = ["derive"] } +serde = { version = "1.0.228", features = ["derive"] } +serde_json = "1.0.145" [package.metadata.deb] maintainer = "erique.dev " diff --git a/src/main.rs b/src/main.rs index 2dc2760..42dfcdc 100644 --- a/src/main.rs +++ b/src/main.rs @@ -13,7 +13,7 @@ use std::io::{self, Write}; use std::fs; use clap::{Parser, Subcommand}; -use std::path::Path; +use serde::{Serialize, Deserialize}; const BANNER: &str = concat!("\x1b[32m", r#" @@ -52,23 +52,62 @@ enum Commands { /// Show version information Version, } +#[derive(Serialize, Deserialize, Debug)] +struct LogicalLayer { + name: String, +} + +#[derive(Serialize, Deserialize, Debug)] +struct PhysicalLayer { + name: String, + logical_layers: Vec, +} + +#[derive(Serialize, Deserialize, Debug)] +struct ProjectSchema { + project_name: String, + physical_layers: Vec, +} fn init_project() -> io::Result<()> { println!("{}", BANNER); - let project_name = prompt_required("Project name (e.g.: myAPI)"); - let project_name = to_pascal_case(&project_name); - create_path(&project_name)?; - println!("crated folder {}", project_name); - Ok(()) -} -fn create_path(name: &str) -> io::Result<()> { - let path = Path::new(name); - if path.exists() { - println!("'{}' already exists", name); - } else { - fs::create_dir(path)?; + let project_name = prompt_required("Project name (e.g.: MyAPI)"); + + let num_physical = prompt_number("How many physical layers?"); + let mut physical_layers = Vec::new(); + + for i in 0..num_physical { + println!("\n→ Physical layer #{}:", i + 1); + let physical_name = prompt_required(" Name of physical layer"); + + let num_logical = prompt_number(" How many logical layers?"); + let mut logical_layers = Vec::new(); + + for j in 0..num_logical { + let logical_name = prompt_required(&format!(" Name of logical layer #{}", j + 1)); + logical_layers.push(LogicalLayer { name: logical_name }); + } + + physical_layers.push(PhysicalLayer { + name: physical_name, + logical_layers, + }); } + + let project_schema = ProjectSchema { + project_name: project_name.clone(), + physical_layers, + }; + + let json = serde_json::to_string_pretty(&project_schema).unwrap(); + + let file_name = format!("{}_schema.json", project_name); + fs::write(&file_name, &json)?; + + println!("\n✅ Generated JSON schema:\n{}", json); + println!("\n💾 Saved to file: '{}'", file_name); + Ok(()) } @@ -87,11 +126,24 @@ fn prompt(label: &str, default: Option<&str>) -> String { print!("{}{}: ", label, default.map(|d| format!(" [{}]", d)).unwrap_or_default()); io::stdout().flush().unwrap(); io::stdin().read_line(&mut input).unwrap(); - let s = input.trim().to_string(); + let s = input.trim(); if s.is_empty() { - default.unwrap_or("").to_string() + default.unwrap_or("DeveloperStartSpringboot").to_string() } else { - s + to_pascal_case(s) + } +} + +fn prompt_number(label: &str) -> usize { + loop { + print!("{}: ", label); + io::stdout().flush().unwrap(); + let mut input = String::new(); + io::stdin().read_line(&mut input).unwrap(); + if let Ok(num) = input.trim().parse::() { + return num; + } + println!("Please enter a valid number."); } } @@ -102,7 +154,11 @@ fn to_pascal_case(input: &str) -> String { .map(|word| { let mut chars = word.chars(); match chars.next() { - Some(first) => first.to_uppercase().collect::() + &chars.as_str().to_lowercase(), + Some(first) => { + let first = first.to_uppercase().collect::(); + let rest = chars.collect::().to_lowercase(); + format!("{}{}", first, rest) + } None => String::new(), } }) @@ -115,7 +171,7 @@ fn main() { match &cli.command { Commands::Init => { if let Err(e) = init_project() { - eprintln!("Error initializing project: {}", e); + eprintln!("Error generating JSON: {}", e); std::process::exit(1); } } From 448f709febd67ac09249dda5160a867d5e1e0144 Mon Sep 17 00:00:00 2001 From: "erique.rocha" Date: Fri, 7 Nov 2025 14:27:24 -0300 Subject: [PATCH 3/3] feat: Use JSON schema to create folder schema --- src/main.rs | 225 ++++++++++++++++++++++++++++++++-------------------- 1 file changed, 139 insertions(+), 86 deletions(-) diff --git a/src/main.rs b/src/main.rs index 42dfcdc..8ebafb7 100644 --- a/src/main.rs +++ b/src/main.rs @@ -10,10 +10,11 @@ * See the LICENSE file for more details. */ -use std::io::{self, Write}; -use std::fs; use clap::{Parser, Subcommand}; -use serde::{Serialize, Deserialize}; +use serde::{Deserialize, Serialize}; +use std::fs; +use std::fs::create_dir_all; +use std::io::{self, Write}; const BANNER: &str = concat!("\x1b[32m", r#" @@ -39,7 +40,7 @@ const BANNER: &str = concat!("\x1b[32m", r#" #[derive(Parser)] #[command(name = "dss")] -#[command(about = "Spring Boot project generator", long_about = None)] +#[command(about = "Spring Boot project generator")] struct Cli { #[command(subcommand)] command: Commands, @@ -47,11 +48,10 @@ struct Cli { #[derive(Subcommand)] enum Commands { - /// Initialize a new Spring Boot project Init, - /// Show version information Version, } + #[derive(Serialize, Deserialize, Debug)] struct LogicalLayer { name: String, @@ -69,114 +69,167 @@ struct ProjectSchema { physical_layers: Vec, } +fn main() { + match Cli::parse().command { + Commands::Init => { + if let Err(e) = init_project() { + eprintln!("Error: {e}"); + std::process::exit(1); + } + } + Commands::Version => println!("dss version 0.3.0"), + } +} + +/* ---------- helpers de formatação ---------- */ + +fn to_pascal_case(s: &str) -> String { + s.split(|c: char| c == ' ' || c == '-' || c == '_') + .filter(|seg| !seg.is_empty()) + .map(|seg| { + let mut ch = seg.chars(); + match ch.next() { + Some(f) => f.to_uppercase().collect::() + &ch.as_str().to_lowercase(), + None => String::new(), + } + }) + .collect() +} + +fn to_camel_case(s: &str) -> String { + let p = to_pascal_case(s); + let mut ch = p.chars(); + match ch.next() { + Some(f) => f.to_lowercase().collect::() + ch.as_str(), + None => String::new(), + } +} + +fn domain_to_path(domain: &str) -> String { + domain + .trim() + .trim_start_matches("www.") + .split('.') + .filter(|seg| !seg.is_empty()) + .rev() + .map(|seg| seg.to_lowercase()) + .collect::>() + .join("/") +} + +/* ---------- prompts ---------- */ + +fn prompt_line(label: &str, default: Option<&str>) -> String { + loop { + print!( + "{}{}: ", + label, + default.map(|d| format!(" [{d}]")).unwrap_or_default() + ); + io::stdout().flush().unwrap(); + + let mut buf = String::new(); + io::stdin().read_line(&mut buf).unwrap(); + let input = buf.trim(); + + if !input.is_empty() { + return input.to_string(); + } + if let Some(def) = default { + return def.to_string(); + } + println!("This field is required. Please fill it in."); + } +} + +fn prompt_number(label: &str) -> usize { + loop { + print!("{label}: "); + io::stdout().flush().unwrap(); + let mut buf = String::new(); + io::stdin().read_line(&mut buf).unwrap(); + if let Ok(n) = buf.trim().parse::() { + return n; + } + println!("Please enter a valid number."); + } +} + +/* ---------- geração principal ---------- */ + fn init_project() -> io::Result<()> { - println!("{}", BANNER); + println!("{BANNER}"); + + //coleta + let project_name_raw = prompt_line("Project name (e.g.: MyAPI)", None); + let domain_raw = prompt_line("Domain (e.g.: example.com)", None); - let project_name = prompt_required("Project name (e.g.: MyAPI)"); + let project_name = to_pascal_case(&project_name_raw); + let domain_path = domain_to_path(&domain_raw); let num_physical = prompt_number("How many physical layers?"); - let mut physical_layers = Vec::new(); + let mut physical_layers = Vec::with_capacity(num_physical); for i in 0..num_physical { println!("\n→ Physical layer #{}:", i + 1); - let physical_name = prompt_required(" Name of physical layer"); - + let phys_name = prompt_line(" Name of physical layer", None); let num_logical = prompt_number(" How many logical layers?"); - let mut logical_layers = Vec::new(); + let mut logical_layers = Vec::with_capacity(num_logical); for j in 0..num_logical { - let logical_name = prompt_required(&format!(" Name of logical layer #{}", j + 1)); - logical_layers.push(LogicalLayer { name: logical_name }); + let log_name = prompt_line(&format!(" Name of logical layer #{}", j + 1), None); + logical_layers.push(LogicalLayer { name: log_name }); } - physical_layers.push(PhysicalLayer { - name: physical_name, + name: phys_name, logical_layers, }); } - let project_schema = ProjectSchema { + //JSON + let schema = ProjectSchema { project_name: project_name.clone(), physical_layers, }; + let json = serde_json::to_string_pretty(&schema).unwrap(); + let json_file = format!("{}_schema.json", project_name); + fs::write(&json_file, &json)?; + println!("\n-- Generated JSON schema:\n{json}"); + println!("-- Saved to file: '{json_file}'"); - let json = serde_json::to_string_pretty(&project_schema).unwrap(); - - let file_name = format!("{}_schema.json", project_name); - fs::write(&file_name, &json)?; - - println!("\n✅ Generated JSON schema:\n{}", json); - println!("\n💾 Saved to file: '{}'", file_name); + //pastas + create_dirs(&schema, &domain_path)?; Ok(()) } -fn prompt_required(label: &str) -> String { - loop { - let input = prompt(label, None); - if !input.trim().is_empty() { - return input; - } - println!("This field is required. Please fill it in."); - } -} +/* ---------- criação de diretórios ---------- */ -fn prompt(label: &str, default: Option<&str>) -> String { - let mut input = String::new(); - print!("{}{}: ", label, default.map(|d| format!(" [{}]", d)).unwrap_or_default()); - io::stdout().flush().unwrap(); - io::stdin().read_line(&mut input).unwrap(); - let s = input.trim(); - if s.is_empty() { - default.unwrap_or("DeveloperStartSpringboot").to_string() - } else { - to_pascal_case(s) - } -} +fn create_dirs(schema: &ProjectSchema, domain_path: &str) -> io::Result<()> { + let root = &schema.project_name; + create_dir_all(root)?; + fs::write(format!("{root}/pom.xml"), "")?;//pom raiz -fn prompt_number(label: &str) -> usize { - loop { - print!("{}: ", label); - io::stdout().flush().unwrap(); - let mut input = String::new(); - io::stdin().read_line(&mut input).unwrap(); - if let Ok(num) = input.trim().parse::() { - return num; - } - println!("Please enter a valid number."); - } -} + for phys in &schema.physical_layers { + let phys_camel = to_camel_case(&phys.name); + let phys_dir = format!("{root}/{phys_camel}"); + create_dir_all(&phys_dir)?; -fn to_pascal_case(input: &str) -> String { - input - .split(|c: char| c == ' ' || c == '-' || c == '_') - .filter(|s| !s.is_empty()) - .map(|word| { - let mut chars = word.chars(); - match chars.next() { - Some(first) => { - let first = first.to_uppercase().collect::(); - let rest = chars.collect::().to_lowercase(); - format!("{}{}", first, rest) - } - None => String::new(), - } - }) - .collect::() -} + create_dir_all(format!("{phys_dir}/src/test"))?; + create_dir_all(format!("{phys_dir}/src/main/resource"))?; -fn main() { - let cli = Cli::parse(); + let base_code = format!("{phys_dir}/src/main/{domain_path}/{phys_camel}"); + create_dir_all(&base_code)?; - match &cli.command { - Commands::Init => { - if let Err(e) = init_project() { - eprintln!("Error generating JSON: {}", e); - std::process::exit(1); - } - } - Commands::Version => { - println!("dss version 0.1.0"); + //camadas lógicas + for log in &phys.logical_layers { + let log_camel = to_camel_case(&log.name); + create_dir_all(format!("{base_code}/{log_camel}"))?; } + + fs::write(format!("{phys_dir}/pom.xml"), "")?; } -} \ No newline at end of file + + println!("\n-- Directories created successfully!"); + Ok(()) +}