diff --git a/app/controllers/controllers.py b/app/controllers/controllers.py index 062bf34..a71fd04 100644 --- a/app/controllers/controllers.py +++ b/app/controllers/controllers.py @@ -9,7 +9,7 @@ from app.schema.schema import UserCreate, DomainResponse, Token, CreateUserResponse, LoginData, LoginResponse, PaginatedDomainsResponse, PaginatedSubDomainsResponse, SubdomainSearchResponse from app.database.database import User, Domain, SubDomain, get_database from app.service.search_enumerator import get_subdomain_data, get_updated_domains -from app.service.service import create_new_user, create_access_token, get_user_from_cookie, isAdmin, get_user, login_user, get_my_profile, get_user_domain_with_subdomains, get_user_domains +from app.service.service import create_new_user, create_access_token, get_user_from_cookie, isAdmin, get_user, login_user, get_my_profile, get_user_domain_with_subdomains, get_user_domains, get_auth_user router = APIRouter() @@ -44,7 +44,7 @@ def create_user(user: UserCreate, response: Response, request: Request, db: Sess httponly=True, secure=True, samesite="strict", - max_age=6*60 + max_age=14 * 24 * 60 * 60 ) return new_user @@ -59,10 +59,11 @@ def handle_login_user(data: LoginData, request: Request, response: Response, db: httponly=True, secure=True, samesite="strict", - max_age=6*60, + max_age=14 * 24 * 60 * 60, ) return data + @router.get("/profile/me") @limiter.limit("5/minute") def my_profile(request: Request, user: User = Depends(get_user_from_cookie), db: Session = Depends(get_database)): @@ -72,7 +73,7 @@ def my_profile(request: Request, user: User = Depends(get_user_from_cookie), db: @router.get("/domains", response_model=PaginatedDomainsResponse) @limiter.limit("10/minute") async def get_user_domains( - request:Request, + request: Request, skip: int = Query(0, ge=0), limit: int = Query(10, ge=1, le=100), current_user: User = Depends(get_user_from_cookie), @@ -87,31 +88,31 @@ async def get_user_domains( ) -@router.get("/domains/{name}", response_model=PaginatedSubDomainsResponse) -@limiter.limit("10/minute") +@router.get("/domains/{id}") +@limiter.limit("15/minute") async def read_user_domain( request: Request, - name: str, + id: int, skip: int = Query(0, ge=0), limit: int = Query(10, ge=1, le=100), current_user: User = Depends(get_user_from_cookie), db: Session = Depends(get_database) ): domain, total_subdomains = get_user_domain_with_subdomains( - db, current_user, name, skip, limit) + db, current_user, id, skip, limit) if domain is None: raise HTTPException(status_code=404, detail="Domain cannot be found") - return PaginatedSubDomainsResponse( - domain=domain, - sub_domains=domain.sub_domains, - total_subdomains=total_subdomains, - skip=skip, - limit=limit - ) + return { + "domain": domain, + "sub_domains": domain.sub_domains, + "total_subdomains": total_subdomains, + "skip": skip, + "limit": limit + } @router.get("/domain/check-updates") @limiter.limit("5/minute") -async def get_domain_updates(request:Request, domain:str, user:User = Depends(get_user_from_cookie), db:Session = Depends(get_database)): +async def get_domain_updates(request: Request, domain: str, user: User = Depends(get_user_from_cookie), db: Session = Depends(get_database)): data = await get_updated_domains(db=db, domain=domain, user=user) - return SubdomainSearchResponse(**data) \ No newline at end of file + return SubdomainSearchResponse(**data) diff --git a/app/service/search_enumerator.py b/app/service/search_enumerator.py index 3e2b5cf..260f1ca 100644 --- a/app/service/search_enumerator.py +++ b/app/service/search_enumerator.py @@ -327,6 +327,7 @@ async def run_all_query_async(self): async def get_subdomain_data(domain: str, db: Session, user: User) -> Dict[str, List[str]]: try: + print("is it coming?") parsed_domain = urlparse(f"http://{domain}").netloc res = SubDomainScrapper(parsed_domain) data = await res.run_all_query_async() @@ -355,6 +356,7 @@ async def get_subdomain_data(domain: str, db: Session, user: User) -> Dict[str, "wildcards": sorted(list(res.wildcard_subdomains)) } except Exception as e: + print(e) raise HTTPException( status_code=500, detail=f"An error occurred: {str(e)}") diff --git a/app/service/service.py b/app/service/service.py index da7bbdb..b5efcd7 100644 --- a/app/service/service.py +++ b/app/service/service.py @@ -182,9 +182,9 @@ def get_user_domains(db: Session, user: User, skip: int = 0, limit: int = 10) -> return domains, total -def get_user_domain_with_subdomains(db: Session, user: User, domain_name: str, skip: int = 0, limit: int = 10) -> Tuple[Domain, int]: +def get_user_domain_with_subdomains(db: Session, user: User, id: int, skip: int = 0, limit: int = 10) -> Tuple[Domain, int]: domain:Domain = db.query(Domain).filter( - Domain.domain_name == domain_name, + Domain.id == id, Domain.user_id == user.id ).first() diff --git a/app/static/css/profile.css b/app/static/css/profile.css index a46f09a..4fca163 100644 --- a/app/static/css/profile.css +++ b/app/static/css/profile.css @@ -1,25 +1,28 @@ -.domain-table { +.domain-table, .subdomain-table { width: 100%; border-collapse: collapse; margin-top: 20px; } -.domain-table th, .domain-table td { +.domain-table th, .domain-table td, +.subdomain-table th, .subdomain-table td { border: 1px solid #ddd; padding: 12px; text-align: left; } -.domain-table th { +.domain-table th, .subdomain-table th { background-color: #f2f2f2; font-weight: bold; } -.domain-table tr:nth-child(even) { +.domain-table tr:nth-child(even), +.subdomain-table tr:nth-child(even) { background-color: #f9f9f9; } -.domain-table tr:hover { +.domain-table tr:hover, +.subdomain-table tr:hover { background-color: #f5f5f5; } @@ -43,4 +46,61 @@ .view-btn:hover, .delete-btn:hover { opacity: 0.8; +} + +#pagination { + margin-top: 20px; + text-align: center; +} + +#pagination button, #pagination span { + margin: 0 5px; + padding: 5px 10px; + border: 1px solid #ddd; + background-color: #f8f8f8; + cursor: pointer; +} + +#pagination span { + background-color: #4CAF50; + color: white; + border-color: #4CAF50; +} + +#pagination button:hover { + background-color: #ddd; +} + +.modal { + display: none; + position: fixed; + z-index: 1; + left: 0; + top: 0; + width: 100%; + height: 100%; + overflow: auto; + background-color: rgba(0,0,0,0.4); +} + +.modal-content { + background-color: #fefefe; + margin: 15% auto; + padding: 20px; + border: 1px solid #888; + width: 80%; +} + +.close { + color: #aaa; + float: right; + font-size: 28px; + font-weight: bold; +} + +.close:hover, +.close:focus { + color: black; + text-decoration: none; + cursor: pointer; } \ No newline at end of file diff --git a/app/static/css/style.css b/app/static/css/style.css index 429a1ab..362d4a7 100644 --- a/app/static/css/style.css +++ b/app/static/css/style.css @@ -146,16 +146,6 @@ nav ul li a { margin-bottom: 1rem; } -.results { - padding: 4rem 0; -} - -.results h3 { - text-align: center; - font-size: 2rem; - margin-bottom: 2rem; -} - #subdomainList { list-style: none; display: grid; @@ -179,10 +169,6 @@ footer { margin-top: auto; } -.hidden { - display: none; -} - @media (max-width: 768px) { header .container { flex-direction: column; @@ -200,3 +186,80 @@ footer { width: 100%; } } +.results { + padding: 4rem 0; +} + +.results h3 { + text-align: center; + font-size: 2rem; + margin-bottom: 2rem; +} + +#resultsContent { + background-color: #fff; + padding: 2rem; + border-radius: 5px; + box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1); +} + +#resultsContent p { + font-size: 1.2rem; + margin-bottom: 1rem; +} + +#resultsContent h4 { + color: var(--primary-color); + margin-top: 1.5rem; + margin-bottom: 1rem; +} + +#resultsContent ul { + list-style: none; + display: grid; + grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); + gap: 1rem; +} + +#resultsContent li { + background-color: var(--secondary-color); + padding: 0.5rem 1rem; + border-radius: 5px; +} + +.hidden { + display: none; +} + +.loader { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + padding: 2rem 0; +} + +.spinner { + border: 4px solid var(--secondary-color); + border-top: 4px solid var(--primary-color); + border-radius: 50%; + width: 40px; + height: 40px; + animation: spin 1s linear infinite; +} + +@keyframes spin { + 0% { transform: rotate(0deg); } + 100% { transform: rotate(360deg); } +} + +.loader p { + margin-top: 1rem; + font-size: 1.2rem; + color: var(--primary-color); +} + +/* Ensure hidden class actually hides the element */ +.hidden { + display: none !important; +} \ No newline at end of file diff --git a/app/static/js/login.js b/app/static/js/login.js index 34a4337..ba78fb1 100644 --- a/app/static/js/login.js +++ b/app/static/js/login.js @@ -23,75 +23,28 @@ document.addEventListener("DOMContentLoaded", function () { const email = document.getElementById("login-email").value; console.log(email); const password = document.getElementById("login-password").value; - console.log(password); - // document.getElementById("loginMessage").textContent = - // HandleAuthInputValidation(email, password, "loginMessage"); + document.getElementById("loginMessage").textContent = + HandleAuthInputValidation(email, password, "loginMessage"); const data = await handleAuthRequest(email, password, "login") // const data = await loginRequest(email, password) - document.getElementById("loginMessage").textContent = data - // try { - // const response = await fetch("/api/v1/token", { - // method: "POST", - // headers: { "Content-Type": "application/x-www-form-urlencoded" }, - // body: `email=${encodeURIComponent( - // email - // )}&password=${encodeURIComponent(password)}`, - // }); - // if (response.ok) { - // window.location.href = "/profile"; - // } else { - // const data = await response.json(); - // document.getElementById("loginMessage").textContent = - // data.detail || "Login failed"; - // } - // } catch (error) { - // console.error("Login error:", error); - // document.getElementById("loginMessage").textContent = - // "An error occurred. Please try again."; - // } + document.getElementById("loginMessage").textContent = data.detail }); document .querySelector("#registerForm form") .addEventListener("submit", async (e) => { e.preventDefault(); - // const username = document.getElementById("register-username").value; const email = document.getElementById("register-email").value; const password = document.getElementById("register-password").value; - const confirmPassword = document.getElementById( - "register-confirm-password" - ).value; - - if (password !== confirmPassword) { - document.getElementById("registerMessage").textContent = - "Passwords do not match"; - return; - } - - try { - const response = await fetch("/api/v1/users", { - method: "POST", - headers: { "Content-Type": "application/json" }, - body: JSON.stringify({ email, password }), - }); - if (response.ok) { - document.getElementById("registerMessage").textContent = - "Registration successful! Please login."; - } else { - const data = await response.json(); - document.getElementById("registerMessage").textContent = - data.detail || "Registration failed"; - } - } catch (error) { - console.error("Registration error:", error); - document.getElementById("registerMessage").textContent = - "An error occurred. Please try again."; - } + + document.getElementById("registerMessage").textContent = + HandleAuthInputValidation(email, password, "registerMessage"); + const data = await handleAuthRequest(email, password, "signup"); + document.getElementById("registerMessage").textContent = data.detail; }); }); function HandleAuthInputValidation(email, password, element) { - console.log("hello here", email); const messageElement = document.getElementById(element); messageElement.textContent = ""; @@ -132,13 +85,4 @@ export async function handleAuthRequest(email, password, type) { alert(error); console.log(error); } -} - - -/* -test data -{ - "email": "a11@gmail.com", - "password": "some_password" -} -*/ \ No newline at end of file +} \ No newline at end of file diff --git a/app/static/js/main.js b/app/static/js/main.js index e69de29..c839d1a 100644 --- a/app/static/js/main.js +++ b/app/static/js/main.js @@ -0,0 +1,63 @@ +document.addEventListener("DOMContentLoaded", function () { + const searchForm = document.getElementById("searchForm"); + const resultsSection = document.getElementById("results"); + const resultsContent = document.getElementById("resultsContent"); + const loader = document.getElementById("loader"); + + loader.classList.add("hidden"); + + searchForm.addEventListener("submit", async function (e) { + e.preventDefault(); + const domain = document.getElementById("domainInput").value; + + // Show loader only when search is initiated + loader.classList.remove("hidden"); + resultsSection.classList.add("hidden"); + + try { + const response = await fetch( + `/api/v1/search?domain=${encodeURIComponent(domain)}` + ); + + if (response.status === 401) { + window.location.href = "/auth/login"; + return; + } + + if (!response.ok) { + throw new Error("Search failed"); + } + + const data = await response.json(); + displayResults(data); + } catch (error) { + console.error("Error:", error); + resultsContent.innerHTML = + "
An error occurred while searching. Please try again.
"; + resultsSection.classList.remove("hidden"); + } finally { + loader.classList.add("hidden"); + } + }); + + function displayResults(data) { + let html = ` +Total subdomains found: ${data.count}
+Subdomain Name | +Status | +Created Date | +
---|---|---|
${subdomain.name} | +${subdomain.isActive ? "Active" : "Active"} | +${new Date(subdomain.createdDate).toLocaleString()} | +