UNPKG

@re-shell/cli

Version:

Full-stack development platform uniting microservices and microfrontends. Build complete applications with .NET (ASP.NET Core Web API, Minimal API), Java (Spring Boot, Quarkus, Micronaut, Vert.x), Rust (Actix-Web, Warp, Rocket, Axum), Python (FastAPI, Dja

1,446 lines (1,247 loc) 62 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.vertxTemplate = void 0; exports.vertxTemplate = { id: 'vertx', name: 'Vert.x', displayName: 'Vert.x Reactive', description: 'Reactive JVM application framework for building responsive, resilient, elastic, and message-driven applications', version: '4.5.0', framework: 'vertx', language: 'java', port: 8080, tags: ['reactive', 'event-driven', 'non-blocking', 'microservices', 'high-performance'], features: [ 'authentication', 'authorization', 'database', 'caching', 'logging', 'monitoring', 'testing', 'documentation', 'security', 'validation', 'rest-api', 'websockets', 'microservices', 'docker' ], dependencies: {}, devDependencies: {}, postInstall: [ 'echo "✅ Vert.x project created successfully!"', 'echo "📦 Installing dependencies..."', './mvnw clean compile', 'echo "🚀 Start development server: ./mvnw exec:java"', 'echo "📚 API Documentation: http://localhost:{{port}}/swagger-ui"' ], files: { 'pom.xml': `<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>{{packageName}}</groupId> <artifactId>{{serviceName}}</artifactId> <version>1.0.0-SNAPSHOT</version> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <maven.compiler.source>17</maven.compiler.source> <maven.compiler.target>17</maven.compiler.target> <vertx.version>4.5.0</vertx.version> <junit-jupiter.version>5.10.1</junit-jupiter.version> <testcontainers.version>1.19.3</testcontainers.version> <main.verticle>{{packageName}}.MainVerticle</main.verticle> <launcher.class>io.vertx.core.Launcher</launcher.class> </properties> <dependencyManagement> <dependencies> <dependency> <groupId>io.vertx</groupId> <artifactId>vertx-stack-depchain</artifactId> <version>\${vertx.version}</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> <dependencies> <!-- Vert.x Core --> <dependency> <groupId>io.vertx</groupId> <artifactId>vertx-core</artifactId> </dependency> <dependency> <groupId>io.vertx</groupId> <artifactId>vertx-web</artifactId> </dependency> <dependency> <groupId>io.vertx</groupId> <artifactId>vertx-web-validation</artifactId> </dependency> <!-- Security --> <dependency> <groupId>io.vertx</groupId> <artifactId>vertx-auth-jwt</artifactId> </dependency> <dependency> <groupId>io.vertx</groupId> <artifactId>vertx-auth-common</artifactId> </dependency> <!-- Database --> <dependency> <groupId>io.vertx</groupId> <artifactId>vertx-pg-client</artifactId> </dependency> <dependency> <groupId>io.vertx</groupId> <artifactId>vertx-sql-client-templates</artifactId> </dependency> <dependency> <groupId>org.flywaydb</groupId> <artifactId>flyway-core</artifactId> <version>9.22.3</version> </dependency> <dependency> <groupId>org.postgresql</groupId> <artifactId>postgresql</artifactId> <version>42.7.1</version> </dependency> <!-- Redis --> <dependency> <groupId>io.vertx</groupId> <artifactId>vertx-redis-client</artifactId> </dependency> <!-- Configuration --> <dependency> <groupId>io.vertx</groupId> <artifactId>vertx-config</artifactId> </dependency> <dependency> <groupId>io.vertx</groupId> <artifactId>vertx-config-yaml</artifactId> </dependency> <!-- OpenAPI --> <dependency> <groupId>io.vertx</groupId> <artifactId>vertx-web-openapi</artifactId> </dependency> <!-- Health Checks --> <dependency> <groupId>io.vertx</groupId> <artifactId>vertx-health-check</artifactId> </dependency> <!-- Metrics --> <dependency> <groupId>io.vertx</groupId> <artifactId>vertx-micrometer-metrics</artifactId> </dependency> <dependency> <groupId>io.micrometer</groupId> <artifactId>micrometer-registry-prometheus</artifactId> <version>1.12.0</version> </dependency> <!-- Service Discovery --> <dependency> <groupId>io.vertx</groupId> <artifactId>vertx-service-discovery</artifactId> </dependency> <!-- Circuit Breaker --> <dependency> <groupId>io.vertx</groupId> <artifactId>vertx-circuit-breaker</artifactId> </dependency> <!-- JSON --> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>2.16.0</version> </dependency> <dependency> <groupId>com.fasterxml.jackson.datatype</groupId> <artifactId>jackson-datatype-jsr310</artifactId> <version>2.16.0</version> </dependency> <!-- Logging --> <dependency> <groupId>ch.qos.logback</groupId> <artifactId>logback-classic</artifactId> <version>1.4.14</version> </dependency> <!-- Utils --> <dependency> <groupId>org.mindrot</groupId> <artifactId>jbcrypt</artifactId> <version>0.4</version> </dependency> <!-- Testing --> <dependency> <groupId>io.vertx</groupId> <artifactId>vertx-junit5</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>io.vertx</groupId> <artifactId>vertx-web-client</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>org.junit.jupiter</groupId> <artifactId>junit-jupiter</artifactId> <version>\${junit-jupiter.version}</version> <scope>test</scope> </dependency> <dependency> <groupId>org.mockito</groupId> <artifactId>mockito-core</artifactId> <version>5.7.0</version> <scope>test</scope> </dependency> <dependency> <groupId>org.testcontainers</groupId> <artifactId>testcontainers</artifactId> <version>\${testcontainers.version}</version> <scope>test</scope> </dependency> <dependency> <groupId>org.testcontainers</groupId> <artifactId>postgresql</artifactId> <version>\${testcontainers.version}</version> <scope>test</scope> </dependency> </dependencies> <build> <plugins> <plugin> <artifactId>maven-compiler-plugin</artifactId> <version>3.11.0</version> <configuration> <source>\${maven.compiler.source}</source> <target>\${maven.compiler.target}</target> </configuration> </plugin> <plugin> <artifactId>maven-shade-plugin</artifactId> <version>3.5.1</version> <executions> <execution> <phase>package</phase> <goals> <goal>shade</goal> </goals> <configuration> <transformers> <transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer"> <manifestEntries> <Main-Class>\${launcher.class}</Main-Class> <Main-Verticle>\${main.verticle}</Main-Verticle> </manifestEntries> </transformer> <transformer implementation="org.apache.maven.plugins.shade.resource.ServicesResourceTransformer"/> </transformers> <outputFile>\${project.build.directory}/\${project.artifactId}-\${project.version}-fat.jar</outputFile> </configuration> </execution> </executions> </plugin> <plugin> <artifactId>maven-surefire-plugin</artifactId> <version>3.2.2</version> </plugin> <plugin> <groupId>org.codehaus.mojo</groupId> <artifactId>exec-maven-plugin</artifactId> <version>3.1.1</version> <configuration> <mainClass>\${launcher.class}</mainClass> <arguments> <argument>run</argument> <argument>\${main.verticle}</argument> </arguments> </configuration> </plugin> </plugins> </build> </project> `, 'src/main/java/{{packagePath}}/MainVerticle.java': `package {{packageName}}; import {{packageName}}.config.DbConfig; import {{packageName}}.handler.*; import {{packageName}}.repository.UserRepository; import {{packageName}}.service.*; import io.vertx.core.AbstractVerticle; import io.vertx.core.Promise; import io.vertx.core.http.HttpServer; import io.vertx.core.json.JsonObject; import io.vertx.ext.auth.PubSecKeyOptions; import io.vertx.ext.auth.jwt.JWTAuth; import io.vertx.ext.auth.jwt.JWTAuthOptions; import io.vertx.ext.healthchecks.HealthCheckHandler; import io.vertx.ext.healthchecks.HealthChecks; import io.vertx.ext.web.Router; import io.vertx.ext.web.handler.*; import io.vertx.ext.web.openapi.RouterBuilder; import io.vertx.micrometer.PrometheusScrapingHandler; import io.vertx.pgclient.PgPool; import io.vertx.redis.client.Redis; import io.vertx.redis.client.RedisOptions; import org.flywaydb.core.Flyway; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class MainVerticle extends AbstractVerticle { private static final Logger LOG = LoggerFactory.getLogger(MainVerticle.class); @Override public void start(Promise<Void> startPromise) { // Run database migrations runMigrations(); // Initialize database pool PgPool pgPool = DbConfig.createPgPool(vertx, config()); // Initialize Redis client Redis redis = Redis.createClient( vertx, new RedisOptions() .setConnectionString(config().getString("redis.uri", "redis://localhost:6379")) ); // Initialize JWT Auth JWTAuth jwtAuth = JWTAuth.create(vertx, new JWTAuthOptions() .addPubSecKey(new PubSecKeyOptions() .setAlgorithm("HS256") .setBuffer(config().getString("jwt.secret", "change-this-secret")) )); // Initialize repositories UserRepository userRepository = new UserRepository(pgPool); // Initialize services PasswordService passwordService = new PasswordService(); UserService userService = new UserService(userRepository, passwordService); AuthService authService = new AuthService(userService, jwtAuth, config()); CacheService cacheService = new CacheService(redis); // Build router from OpenAPI spec RouterBuilder.create(vertx, "openapi.yaml") .onSuccess(routerBuilder -> { // Create main router Router router = Router.router(vertx); // Add global handlers router.route().handler(BodyHandler.create()); router.route().handler(LoggerHandler.create()); router.route().handler(TimeoutHandler.create(30000)); router.route().handler(ResponseContentTypeHandler.create()); // CORS configuration router.route().handler(CorsHandler.create() .addOrigin("http://localhost:3000") .addOrigin("http://localhost:5173") .allowedMethod(io.vertx.core.http.HttpMethod.GET) .allowedMethod(io.vertx.core.http.HttpMethod.POST) .allowedMethod(io.vertx.core.http.HttpMethod.PUT) .allowedMethod(io.vertx.core.http.HttpMethod.DELETE) .allowedMethod(io.vertx.core.http.HttpMethod.OPTIONS) .allowedHeader("*") .allowCredentials(true)); // Initialize handlers AuthHandler authHandler = new AuthHandler(authService); UserHandler userHandler = new UserHandler(userService, cacheService); // Authentication endpoints router.post("/api/auth/register").handler(authHandler::register); router.post("/api/auth/login").handler(authHandler::login); router.post("/api/auth/refresh").handler(authHandler::refresh); // Protected routes Router apiRouter = Router.router(vertx); apiRouter.route().handler(JWTAuthHandler.create(jwtAuth)); // User endpoints apiRouter.get("/users").handler(userHandler::getAll); apiRouter.get("/users/:id").handler(userHandler::getById); apiRouter.post("/users").handler(userHandler::create); apiRouter.put("/users/:id").handler(userHandler::update); apiRouter.delete("/users/:id").handler(userHandler::delete); router.mountSubRouter("/api", apiRouter); // Health checks HealthCheckHandler healthCheckHandler = HealthCheckHandler.create(vertx); HealthChecks healthChecks = HealthChecks.create(vertx); healthChecks.register("database", promise -> { pgPool.query("SELECT 1").execute(ar -> { if (ar.succeeded()) { promise.complete(); } else { promise.fail(ar.cause()); } }); }); healthChecks.register("redis", promise -> { redis.ping(ar -> { if (ar.succeeded()) { promise.complete(); } else { promise.fail(ar.cause()); } }); }); router.get("/health").handler(healthCheckHandler); router.get("/health/live").handler(ctx -> ctx.response().end("OK")); router.get("/health/ready").handler(healthCheckHandler); // Metrics endpoint router.route("/metrics").handler(PrometheusScrapingHandler.create()); // OpenAPI UI router.get("/swagger-ui/*").handler( StaticHandler.create("webroot/swagger-ui") ); // Mount OpenAPI router router.mountSubRouter("/", routerBuilder.createRouter()); // Create HTTP server HttpServer server = vertx.createHttpServer(); server.requestHandler(router) .listen(config().getInteger("http.port", {{port}})) .onSuccess(http -> { LOG.info("HTTP server started on port " + http.actualPort()); startPromise.complete(); }) .onFailure(startPromise::fail); }) .onFailure(startPromise::fail); } private void runMigrations() { JsonObject dbConfig = config().getJsonObject("database"); String jdbcUrl = String.format("jdbc:postgresql://%s:%d/%s", dbConfig.getString("host", "localhost"), dbConfig.getInteger("port", 5432), dbConfig.getString("database", "{{serviceName}}_db") ); Flyway flyway = Flyway.configure() .dataSource(jdbcUrl, dbConfig.getString("user", "postgres"), dbConfig.getString("password", "postgres")) .load(); flyway.migrate(); LOG.info("Database migrations completed"); } } `, 'src/main/java/{{packagePath}}/config/DbConfig.java': `package {{packageName}}.config; import io.vertx.core.Vertx; import io.vertx.core.json.JsonObject; import io.vertx.pgclient.PgConnectOptions; import io.vertx.pgclient.PgPool; import io.vertx.sqlclient.PoolOptions; public class DbConfig { public static PgPool createPgPool(Vertx vertx, JsonObject config) { JsonObject dbConfig = config.getJsonObject("database", new JsonObject()); PgConnectOptions connectOptions = new PgConnectOptions() .setPort(dbConfig.getInteger("port", 5432)) .setHost(dbConfig.getString("host", "localhost")) .setDatabase(dbConfig.getString("database", "{{serviceName}}_db")) .setUser(dbConfig.getString("user", "postgres")) .setPassword(dbConfig.getString("password", "postgres")); PoolOptions poolOptions = new PoolOptions() .setMaxSize(dbConfig.getInteger("maxPoolSize", 10)); return PgPool.pool(vertx, connectOptions, poolOptions); } } `, 'src/main/java/{{packagePath}}/model/User.java': `package {{packageName}}.model; import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonProperty; import io.vertx.codegen.annotations.DataObject; import io.vertx.core.json.JsonObject; import java.time.LocalDateTime; import java.util.Set; @DataObject public class User { private Long id; private String username; private String email; @JsonProperty(access = JsonProperty.Access.WRITE_ONLY) private String password; private String firstName; private String lastName; private Set<String> roles; private boolean enabled; private LocalDateTime createdAt; private LocalDateTime updatedAt; public User() {} public User(JsonObject json) { this.id = json.getLong("id"); this.username = json.getString("username"); this.email = json.getString("email"); this.password = json.getString("password"); this.firstName = json.getString("first_name"); this.lastName = json.getString("last_name"); this.enabled = json.getBoolean("enabled", true); // Handle roles and timestamps conversion } public JsonObject toJson() { JsonObject json = new JsonObject() .put("id", id) .put("username", username) .put("email", email) .put("first_name", firstName) .put("last_name", lastName) .put("enabled", enabled); if (roles != null) { json.put("roles", roles); } if (createdAt != null) { json.put("created_at", createdAt.toString()); } if (updatedAt != null) { json.put("updated_at", updatedAt.toString()); } return json; } // Getters and setters public Long getId() { return id; } public void setId(Long id) { this.id = id; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } 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 getFirstName() { return firstName; } public void setFirstName(String firstName) { this.firstName = firstName; } public String getLastName() { return lastName; } public void setLastName(String lastName) { this.lastName = lastName; } public Set<String> getRoles() { return roles; } public void setRoles(Set<String> roles) { this.roles = roles; } public boolean isEnabled() { return enabled; } public void setEnabled(boolean enabled) { this.enabled = enabled; } 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; } } `, 'src/main/java/{{packagePath}}/repository/UserRepository.java': `package {{packageName}}.repository; import {{packageName}}.model.User; import io.vertx.core.Future; import io.vertx.pgclient.PgPool; import io.vertx.sqlclient.Row; import io.vertx.sqlclient.RowSet; import io.vertx.sqlclient.Tuple; import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Set; public class UserRepository { private final PgPool pool; public UserRepository(PgPool pool) { this.pool = pool; } public Future<User> findById(Long id) { return pool.preparedQuery( "SELECT * FROM users WHERE id = $1" ).execute(Tuple.of(id)) .map(rows -> { if (rows.size() == 0) { return null; } return mapRowToUser(rows.iterator().next()); }); } public Future<User> findByUsername(String username) { return pool.preparedQuery( "SELECT * FROM users WHERE username = $1" ).execute(Tuple.of(username)) .map(rows -> { if (rows.size() == 0) { return null; } return mapRowToUser(rows.iterator().next()); }); } public Future<User> findByEmail(String email) { return pool.preparedQuery( "SELECT * FROM users WHERE email = $1" ).execute(Tuple.of(email)) .map(rows -> { if (rows.size() == 0) { return null; } return mapRowToUser(rows.iterator().next()); }); } public Future<List<User>> findAll(int limit, int offset) { return pool.preparedQuery( "SELECT * FROM users ORDER BY id LIMIT $1 OFFSET $2" ).execute(Tuple.of(limit, offset)) .map(rows -> { List<User> users = new ArrayList<>(); for (Row row : rows) { users.add(mapRowToUser(row)); } return users; }); } public Future<User> save(User user) { return pool.preparedQuery( "INSERT INTO users (username, email, password, first_name, last_name, roles, enabled) " + "VALUES ($1, $2, $3, $4, $5, $6, $7) RETURNING *" ).execute(Tuple.of( user.getUsername(), user.getEmail(), user.getPassword(), user.getFirstName(), user.getLastName(), String.join(",", user.getRoles()), user.isEnabled() )) .map(rows -> mapRowToUser(rows.iterator().next())); } public Future<User> update(User user) { return pool.preparedQuery( "UPDATE users SET email = $2, first_name = $3, last_name = $4, " + "updated_at = CURRENT_TIMESTAMP WHERE id = $1 RETURNING *" ).execute(Tuple.of( user.getId(), user.getEmail(), user.getFirstName(), user.getLastName() )) .map(rows -> { if (rows.size() == 0) { return null; } return mapRowToUser(rows.iterator().next()); }); } public Future<Boolean> delete(Long id) { return pool.preparedQuery( "DELETE FROM users WHERE id = $1" ).execute(Tuple.of(id)) .map(rows -> rows.rowCount() > 0); } public Future<Boolean> existsByUsername(String username) { return pool.preparedQuery( "SELECT EXISTS(SELECT 1 FROM users WHERE username = $1)" ).execute(Tuple.of(username)) .map(rows -> rows.iterator().next().getBoolean(0)); } public Future<Boolean> existsByEmail(String email) { return pool.preparedQuery( "SELECT EXISTS(SELECT 1 FROM users WHERE email = $1)" ).execute(Tuple.of(email)) .map(rows -> rows.iterator().next().getBoolean(0)); } private User mapRowToUser(Row row) { User user = new User(); user.setId(row.getLong("id")); user.setUsername(row.getString("username")); user.setEmail(row.getString("email")); user.setPassword(row.getString("password")); user.setFirstName(row.getString("first_name")); user.setLastName(row.getString("last_name")); user.setEnabled(row.getBoolean("enabled")); user.setCreatedAt(row.getLocalDateTime("created_at")); user.setUpdatedAt(row.getLocalDateTime("updated_at")); String rolesStr = row.getString("roles"); if (rolesStr != null) { Set<String> roles = new HashSet<>(); for (String role : rolesStr.split(",")) { roles.add(role.trim()); } user.setRoles(roles); } return user; } } `, 'src/main/java/{{packagePath}}/service/UserService.java': `package {{packageName}}.service; import {{packageName}}.model.User; import {{packageName}}.repository.UserRepository; import io.vertx.core.Future; import java.util.HashSet; import java.util.List; import java.util.Set; public class UserService { private final UserRepository userRepository; private final PasswordService passwordService; public UserService(UserRepository userRepository, PasswordService passwordService) { this.userRepository = userRepository; this.passwordService = passwordService; } public Future<User> findById(Long id) { return userRepository.findById(id); } public Future<User> findByUsername(String username) { return userRepository.findByUsername(username); } public Future<List<User>> findAll(int limit, int offset) { return userRepository.findAll(limit, offset); } public Future<User> create(User user) { return userRepository.existsByUsername(user.getUsername()) .compose(exists -> { if (exists) { return Future.failedFuture("Username already exists"); } return userRepository.existsByEmail(user.getEmail()); }) .compose(exists -> { if (exists) { return Future.failedFuture("Email already exists"); } // Set default role Set<String> roles = new HashSet<>(); roles.add("ROLE_USER"); user.setRoles(roles); // Hash password user.setPassword(passwordService.hash(user.getPassword())); return userRepository.save(user); }); } public Future<User> update(Long id, User updates) { return userRepository.findById(id) .compose(user -> { if (user == null) { return Future.failedFuture("User not found"); } // Update fields if (updates.getFirstName() != null) { user.setFirstName(updates.getFirstName()); } if (updates.getLastName() != null) { user.setLastName(updates.getLastName()); } if (updates.getEmail() != null && !updates.getEmail().equals(user.getEmail())) { return userRepository.existsByEmail(updates.getEmail()) .compose(exists -> { if (exists) { return Future.failedFuture("Email already exists"); } user.setEmail(updates.getEmail()); return userRepository.update(user); }); } return userRepository.update(user); }); } public Future<Boolean> delete(Long id) { return userRepository.delete(id); } public Future<Boolean> verifyPassword(String username, String password) { return userRepository.findByUsername(username) .compose(user -> { if (user == null) { // Also check email return userRepository.findByEmail(username); } return Future.succeededFuture(user); }) .map(user -> { if (user == null) { return false; } return passwordService.verify(password, user.getPassword()); }); } } `, 'src/main/java/{{packagePath}}/service/AuthService.java': `package {{packageName}}.service; import {{packageName}}.dto.*; import {{packageName}}.model.User; import io.vertx.core.Future; import io.vertx.core.json.JsonObject; import io.vertx.ext.auth.JWTOptions; import io.vertx.ext.auth.jwt.JWTAuth; public class AuthService { private final UserService userService; private final JWTAuth jwtAuth; private final JsonObject config; public AuthService(UserService userService, JWTAuth jwtAuth, JsonObject config) { this.userService = userService; this.jwtAuth = jwtAuth; this.config = config; } public Future<AuthResponse> register(RegisterRequest request) { User user = new User(); user.setUsername(request.getUsername()); user.setEmail(request.getEmail()); user.setPassword(request.getPassword()); user.setFirstName(request.getFirstName()); user.setLastName(request.getLastName()); return userService.create(user) .map(createdUser -> { String token = generateToken(createdUser); String refreshToken = generateRefreshToken(createdUser); AuthResponse response = new AuthResponse(); response.setAccessToken(token); response.setRefreshToken(refreshToken); response.setTokenType("Bearer"); response.setExpiresIn(config.getLong("jwt.expiresIn", 3600L)); response.setUser(createdUser); return response; }); } public Future<AuthResponse> authenticate(LoginRequest request) { return userService.verifyPassword(request.getUsername(), request.getPassword()) .compose(verified -> { if (!verified) { return Future.failedFuture("Invalid credentials"); } return userService.findByUsername(request.getUsername()) .compose(user -> { if (user == null) { return userService.findByEmail(request.getUsername()); } return Future.succeededFuture(user); }); }) .map(user -> { String token = generateToken(user); String refreshToken = generateRefreshToken(user); AuthResponse response = new AuthResponse(); response.setAccessToken(token); response.setRefreshToken(refreshToken); response.setTokenType("Bearer"); response.setExpiresIn(config.getLong("jwt.expiresIn", 3600L)); response.setUser(user); return response; }); } public Future<AuthResponse> refresh(String refreshToken) { // Verify refresh token return Future.future(promise -> { jwtAuth.authenticate(new JsonObject().put("jwt", refreshToken)) .onSuccess(user -> { String username = user.principal().getString("sub"); userService.findByUsername(username) .onSuccess(foundUser -> { String newToken = generateToken(foundUser); AuthResponse response = new AuthResponse(); response.setAccessToken(newToken); response.setRefreshToken(refreshToken); response.setTokenType("Bearer"); response.setExpiresIn(config.getLong("jwt.expiresIn", 3600L)); response.setUser(foundUser); promise.complete(response); }) .onFailure(promise::fail); }) .onFailure(error -> promise.fail("Invalid refresh token")); }); } private String generateToken(User user) { JsonObject claims = new JsonObject() .put("sub", user.getUsername()) .put("email", user.getEmail()) .put("roles", user.getRoles()); JWTOptions options = new JWTOptions() .setExpiresInSeconds(config.getInteger("jwt.expiresIn", 3600)) .setSubject(user.getUsername()); return jwtAuth.generateToken(claims, options); } private String generateRefreshToken(User user) { JsonObject claims = new JsonObject() .put("sub", user.getUsername()) .put("type", "refresh"); JWTOptions options = new JWTOptions() .setExpiresInSeconds(config.getInteger("jwt.refreshExpiresIn", 86400)); return jwtAuth.generateToken(claims, options); } } `, 'src/main/java/{{packagePath}}/service/PasswordService.java': `package {{packageName}}.service; import org.mindrot.jbcrypt.BCrypt; public class PasswordService { public String hash(String password) { return BCrypt.hashpw(password, BCrypt.gensalt()); } public boolean verify(String password, String hashedPassword) { return BCrypt.checkpw(password, hashedPassword); } } `, 'src/main/java/{{packagePath}}/service/CacheService.java': `package {{packageName}}.service; import io.vertx.core.Future; import io.vertx.core.json.JsonObject; import io.vertx.redis.client.Redis; import io.vertx.redis.client.Request; import io.vertx.redis.client.Command; public class CacheService { private final Redis redis; private static final int DEFAULT_TTL = 600; // 10 minutes public CacheService(Redis redis) { this.redis = redis; } public Future<JsonObject> get(String key) { return redis.send(Request.cmd(Command.GET).arg(key)) .map(response -> { if (response == null || response.toString() == null) { return null; } return new JsonObject(response.toString()); }) .recover(error -> Future.succeededFuture(null)); } public Future<Void> set(String key, JsonObject value) { return set(key, value, DEFAULT_TTL); } public Future<Void> set(String key, JsonObject value, int ttl) { return redis.send(Request.cmd(Command.SETEX) .arg(key) .arg(ttl) .arg(value.encode())) .mapEmpty(); } public Future<Void> delete(String key) { return redis.send(Request.cmd(Command.DEL).arg(key)) .mapEmpty(); } public Future<Void> deletePattern(String pattern) { return redis.send(Request.cmd(Command.KEYS).arg(pattern)) .compose(keys -> { if (keys == null || keys.size() == 0) { return Future.succeededFuture(); } Request request = Request.cmd(Command.DEL); for (int i = 0; i < keys.size(); i++) { request.arg(keys.get(i).toString()); } return redis.send(request).mapEmpty(); }); } } `, 'src/main/java/{{packagePath}}/handler/AuthHandler.java': `package {{packageName}}.handler; import {{packageName}}.dto.*; import {{packageName}}.service.AuthService; import io.vertx.core.json.DecodeException; import io.vertx.core.json.JsonObject; import io.vertx.ext.web.RoutingContext; import io.vertx.ext.web.validation.RequestPredicate; import io.vertx.ext.web.validation.ValidationHandler; import io.vertx.ext.web.validation.builder.Bodies; import io.vertx.json.schema.SchemaParser; import io.vertx.json.schema.SchemaRouter; import io.vertx.json.schema.SchemaRouterOptions; import io.vertx.json.schema.draft7.Draft7SchemaParser; public class AuthHandler { private final AuthService authService; public AuthHandler(AuthService authService) { this.authService = authService; } public void register(RoutingContext ctx) { try { RegisterRequest request = ctx.body().asPojo(RegisterRequest.class); if (request.getUsername() == null || request.getUsername().isEmpty()) { ctx.response() .setStatusCode(400) .end(new JsonObject() .put("error", "Username is required") .encode()); return; } authService.register(request) .onSuccess(response -> { ctx.response() .setStatusCode(201) .putHeader("content-type", "application/json") .end(JsonObject.mapFrom(response).encode()); }) .onFailure(error -> { ctx.response() .setStatusCode(400) .end(new JsonObject() .put("error", error.getMessage()) .encode()); }); } catch (DecodeException e) { ctx.response() .setStatusCode(400) .end(new JsonObject() .put("error", "Invalid request body") .encode()); } } public void login(RoutingContext ctx) { try { LoginRequest request = ctx.body().asPojo(LoginRequest.class); authService.authenticate(request) .onSuccess(response -> { ctx.response() .putHeader("content-type", "application/json") .end(JsonObject.mapFrom(response).encode()); }) .onFailure(error -> { ctx.response() .setStatusCode(401) .end(new JsonObject() .put("error", error.getMessage()) .encode()); }); } catch (DecodeException e) { ctx.response() .setStatusCode(400) .end(new JsonObject() .put("error", "Invalid request body") .encode()); } } public void refresh(RoutingContext ctx) { String refreshToken = ctx.request().getParam("token"); if (refreshToken == null || refreshToken.isEmpty()) { ctx.response() .setStatusCode(400) .end(new JsonObject() .put("error", "Refresh token is required") .encode()); return; } authService.refresh(refreshToken) .onSuccess(response -> { ctx.response() .putHeader("content-type", "application/json") .end(JsonObject.mapFrom(response).encode()); }) .onFailure(error -> { ctx.response() .setStatusCode(401) .end(new JsonObject() .put("error", error.getMessage()) .encode()); }); } } `, 'src/main/java/{{packagePath}}/handler/UserHandler.java': `package {{packageName}}.handler; import {{packageName}}.model.User; import {{packageName}}.service.CacheService; import {{packageName}}.service.UserService; import io.vertx.core.json.JsonArray; import io.vertx.core.json.JsonObject; import io.vertx.ext.web.RoutingContext; import java.util.List; public class UserHandler { private final UserService userService; private final CacheService cacheService; public UserHandler(UserService userService, CacheService cacheService) { this.userService = userService; this.cacheService = cacheService; } public void getAll(RoutingContext ctx) { int limit = Integer.parseInt(ctx.request().getParam("limit", "20")); int offset = Integer.parseInt(ctx.request().getParam("offset", "0")); userService.findAll(limit, offset) .onSuccess(users -> { JsonArray jsonArray = new JsonArray(); for (User user : users) { jsonArray.add(user.toJson()); } ctx.response() .putHeader("content-type", "application/json") .end(jsonArray.encode()); }) .onFailure(error -> { ctx.response() .setStatusCode(500) .end(new JsonObject() .put("error", error.getMessage()) .encode()); }); } public void getById(RoutingContext ctx) { String idParam = ctx.pathParam("id"); try { Long id = Long.parseLong(idParam); String cacheKey = "user:" + id; // Check cache first cacheService.get(cacheKey) .compose(cached -> { if (cached != null) { ctx.response() .putHeader("content-type", "application/json") .end(cached.encode()); return null; } return userService.findById(id); }) .onSuccess(user -> { if (user == null) { return; // Already sent response from cache } if (user == null) { ctx.response() .setStatusCode(404) .end(new JsonObject() .put("error", "User not found") .encode()); } else { JsonObject userJson = user.toJson(); // Cache the result cacheService.set(cacheKey, userJson); ctx.response() .putHeader("content-type", "application/json") .end(userJson.encode()); } }) .onFailure(error -> { ctx.response() .setStatusCode(500) .end(new JsonObject() .put("error", error.getMessage()) .encode()); }); } catch (NumberFormatException e) { ctx.response() .setStatusCode(400) .end(new JsonObject() .put("error", "Invalid user ID") .encode()); } } public void create(RoutingContext ctx) { try { User user = ctx.body().asPojo(User.class); userService.create(user) .onSuccess(createdUser -> { ctx.response() .setStatusCode(201) .putHeader("content-type", "application/json") .end(createdUser.toJson().encode()); }) .onFailure(error -> { ctx.response() .setStatusCode(400) .end(new JsonObject() .put("error", error.getMessage()) .encode()); }); } catch (Exception e) { ctx.response() .setStatusCode(400) .end(new JsonObject() .put("error", "Invalid request body") .encode()); } } public void update(RoutingContext ctx) { String idParam = ctx.pathParam("id"); try { Long id = Long.parseLong(idParam); User updates = ctx.body().asPojo(User.class); userService.update(id, updates) .onSuccess(updatedUser -> { if (updatedUser == null) { ctx.response() .setStatusCode(404) .end(new JsonObject() .put("error", "User not found") .encode()); } else { // Invalidate cache cacheService.delete("user:" + id); ctx.response() .putHeader("content-type", "application/json") .end(updatedUser.toJson().encode()); } }) .onFailure(error -> { ctx.response() .setStatusCode(400) .end(new JsonObject() .put("error", error.getMessage()) .encode()); }); } catch (Exception e) { ctx.response() .setStatusCode(400) .end(new JsonObject() .put("error", "Invalid request") .encode()); } } public void delete(RoutingContext ctx) { String idParam = ctx.pathParam("id"); try { Long id = Long.parseLong(idParam); userService.delete(id) .onSuccess(deleted -> { if (deleted) { // Invalidate cache cacheService.delete("user:" + id); ctx.response().setStatusCode(204).end(); } else { ctx.response() .setStatusCode(404) .end(new JsonObject() .put("error", "User not found") .encode()); } }) .onFailure(error -> { ctx.response() .setStatusCode(500) .end(new JsonObject() .put("error", error.getMessage()) .encode()); }); } catch (NumberFormatException e) { ctx.response() .setStatusCode(400) .end(new JsonObject() .put("error", "Invalid user ID") .encode()); } } } `, 'src/main/java/{{packagePath}}/dto/LoginRequest.java': `package {{packageName}}.dto; import com.fasterxml.jackson.annotation.JsonProperty; public class LoginRequest { @JsonProperty("username") private String username; @JsonProperty("password") private String password; public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } } `, 'src/main/java/{{packagePath}}/dto/RegisterRequest.java': `package {{packageName}}.dto; import com.fasterxml.jackson.annotation.JsonProperty; public class RegisterRequest { @JsonProperty("username") private String username; @JsonProperty("email") private String email; @JsonProperty("password") private String password; @JsonProperty("first_name") private String firstName; @JsonProperty("last_name") private String lastName; // Getters and setters public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } 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 getFirstName() { return firstName; } public void setFirstName(String firstName) { this.firstName = firstName; } public String getLastName() { return lastNa