File size: 1,510 Bytes
aaa4ec9
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
import hashlib
import re
import logging
from urllib.parse import urlparse

logger = logging.getLogger(__name__)


def get_namespace_id(url: str) -> str:
    """
    Generates a collision-resistant namespace ID.
    Format: clean_name_MD5hash
    """
    clean_name = re.sub(r"https?://(www\.)?", "", url)
    clean_name = re.sub(r"[^a-zA-Z0-9]", "_", clean_name)
    clean_name = clean_name.strip("_")[:30]

    url_hash = hashlib.md5(url.encode("utf-8")).hexdigest()[:6]

    return f"{clean_name}_{url_hash}"


def is_valid_url(url: str, base_domain: str) -> bool:
    """
    Enforces strict crawl scope.
    """
    try:
        parsed = urlparse(url)
        base_parsed = urlparse(base_domain)

        # 1. Scheme Check
        if parsed.scheme not in ["http", "https"]:
            return False

        # 2. Strict Domain Check (Ends with pattern to prevent 'google.com.evil.com')
        # We allow subdomains (e.g. portal.batstateu.edu.ph)
        if not parsed.netloc.endswith(base_parsed.netloc):
            return False

        # 3. File Extension Check
        ignored_exts = [
            ".pdf",
            ".jpg",
            ".png",
            ".gif",
            ".css",
            ".js",
            ".docx",
            ".xlsx",
            ".xml",
            ".zip",
            ".rar",
            ".mp4",
        ]
        if any(parsed.path.lower().endswith(ext) for ext in ignored_exts):
            return False

        return True
    except Exception:
        return False