diff --git a/README.md b/README.md index 10f5017..125b6d5 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,77 @@ -# Java URL Shortener +# Shorten -See full README content in project documentation. \ No newline at end of file +A lightweight URL shortener built using plain Java with `com.sun.net.httpserver.HttpServer`, H2 Database, and vanilla HTML/CSS/JS frontend. + +## โœ… Features + +* ๐Ÿ”— **Anonymous URL shortening**: Convert long URLs into short ones instantly. +* ๐Ÿง‘โ€๐Ÿ’ป **User login**: Create an account and log in. +* ๐ŸŒ **Custom short URLs**: Logged-in users can create their own alias URLs. +* โ†ช๏ธ **Redirection**: Short URLs automatically redirect to the long ones. + +## ๐Ÿ› ๏ธ Tech Stack + +| Layer | Technology | +| -------- | ----------------------------------------- | +| Frontend | HTML, CSS, JavaScript (no framework) | +| Backend | Java, `com.sun.net.httpserver.HttpServer` | +| Database | H2 using JDBC | +| Logging | SLF4J | +| Testing | JUnit 5, Mockito | +| CI/CD | GitHub Actions | + +``` + +## ๐Ÿš€ Getting Started + +### Prerequisites + +* Java 17+ +* Maven + +### Run Application + +```bash +mvn clean package +java -jar target/shorten.jar +``` + +Visit `http://localhost:8080` in your browser. + +### Database + +* H2 in-memory mode used. +* JDBC with prepared statements. + +### GitHub Actions + +* CI configured in `.github/workflows/ci.yml` to run tests on every PR and merge to `main`. + +## ๐Ÿ”Œ API Endpoints + +| Endpoint | Method | Description | +| --------------- | ------ | ------------------------------ | +| `/shorten` | POST | Shortens a given long URL | +| `/s/{shortUrl}` | GET | Redirects to original long URL | +| `/register` | POST | Register a new user | +| `/login` | POST | Log in existing user | + +## ๐Ÿงช Testing + +* All core components are tested with JUnit 5 +* Mocked database and auth dependencies with Mockito + +## ๐Ÿ“Œ Project Management + +* [ ] GitHub **Issues** created for each task +* [ ] Separate **branches** for every feature +* [ ] Pull Requests with self-review +* [ ] CI runs on every PR and merge + +## ๐Ÿ“– License + +This project is licensed under the MIT License. + +--- + +Built with โค๏ธ using pure Java. diff --git a/pom.xml b/pom.xml index b88700e..7c571a1 100644 --- a/pom.xml +++ b/pom.xml @@ -49,6 +49,12 @@ 5.12.0 test + + org.projectlombok + lombok + 1.18.38 + provided + diff --git a/src/main/java/urlshortener/Server.java b/src/main/java/urlshortener/Server.java index f405286..59cca9c 100644 --- a/src/main/java/urlshortener/Server.java +++ b/src/main/java/urlshortener/Server.java @@ -5,9 +5,11 @@ import java.net.InetSocketAddress; import java.sql.*; +import lombok.extern.slf4j.Slf4j; import urlshortener.config.AppConfig; import urlshortener.service.*; +@Slf4j public class Server { private static Connection conn; @@ -29,7 +31,7 @@ public static void main(String[] args) throws Exception { server.setExecutor(null); server.start(); - System.out.println("Server running on http://localhost:8080/"); + log.info("Server running on http://localhost:8080/"); } public static Connection getConn() { diff --git a/src/main/java/urlshortener/service/LoginHandler.java b/src/main/java/urlshortener/service/LoginHandler.java index 6599d60..fd5033e 100644 --- a/src/main/java/urlshortener/service/LoginHandler.java +++ b/src/main/java/urlshortener/service/LoginHandler.java @@ -2,6 +2,7 @@ import com.sun.net.httpserver.HttpExchange; import com.sun.net.httpserver.HttpHandler; +import lombok.extern.slf4j.Slf4j; import urlshortener.utils.HelperMethods; import java.io.File; @@ -15,6 +16,7 @@ import java.sql.ResultSet; import java.sql.SQLException; +@Slf4j public class LoginHandler implements HttpHandler { private static Connection conn; @@ -52,16 +54,16 @@ public void handle(HttpExchange exchange) throws IOException { ResultSet rs = ps.executeQuery(); if (rs.next()) { - System.out.println("Login successful for user: " + username); + log.info("Login successful for user: {}", username); String encodedUsername = URLEncoder.encode(username, StandardCharsets.UTF_8.toString()); exchange.getResponseHeaders().set("Location", "/index?username=" + encodedUsername); exchange.sendResponseHeaders(302, -1); } else { - System.out.println("Invalid credentials for user: " + username); + log.info("Invalid credentials for user: {}", username); HelperMethods.respond(exchange, 401, "Invalid credentials"); } } catch (SQLException e) { - System.err.println("Error querying user: " + e.getMessage()); + log.error("Error querying user: {}", e.getMessage()); HelperMethods.respond(exchange, 500, "Database error"); } } diff --git a/src/main/java/urlshortener/service/RegisterHandler.java b/src/main/java/urlshortener/service/RegisterHandler.java index d45ed20..654e51e 100644 --- a/src/main/java/urlshortener/service/RegisterHandler.java +++ b/src/main/java/urlshortener/service/RegisterHandler.java @@ -2,6 +2,7 @@ import com.sun.net.httpserver.HttpExchange; import com.sun.net.httpserver.HttpHandler; +import lombok.extern.slf4j.Slf4j; import urlshortener.utils.HelperMethods; import java.io.File; @@ -12,6 +13,7 @@ import java.sql.PreparedStatement; import java.sql.SQLException; +@Slf4j public class RegisterHandler implements HttpHandler { private static Connection conn; @@ -48,14 +50,15 @@ public void handle(HttpExchange exchange) throws IOException { ps.setString(2, password); int rowsAffected = ps.executeUpdate(); if (rowsAffected > 0) { - System.out.println("User registered successfully: " + username); + log.info("User registered successfully: {}", username); exchange.getResponseHeaders().set("Location", "/index"); exchange.sendResponseHeaders(302, -1); } else { + log.error("Failed to register user {}", username); HelperMethods.respond(exchange, 500, "Failed to register user"); } } catch (SQLException e) { - System.err.println("Error inserting user: " + e.getMessage()); + log.error("Error inserting user: {}", e.getMessage()); HelperMethods.respond(exchange, 400, "User already exists"); } }