diff --git a/api/src/routes/ipv4.py b/api/src/routes/ipv4.py
index 65cf943..59b0152 100644
--- a/api/src/routes/ipv4.py
+++ b/api/src/routes/ipv4.py
@@ -3,7 +3,7 @@
from fastapi import APIRouter, Depends
from dependencies.jwt import jwt_bearer
-from utils.IPV4 import (
+from utils.ipv4 import (
bin_to_dec,
dec_to_bin,
dec_to_hex,
diff --git a/api/src/utils/auth.py b/api/src/utils/auth.py
index dab41c2..605b964 100644
--- a/api/src/utils/auth.py
+++ b/api/src/utils/auth.py
@@ -106,7 +106,6 @@ def verify_jwt(token: str) -> dict:
except JWTError as err:
raise HTTPException(status_code=403, detail="Invalid token") from err
-
def validate_password(password: str) -> bool:
"""Return True if the password is valid, False otherwise.
@@ -115,16 +114,7 @@ def validate_password(password: str) -> bool:
Returns:
bool: True if the password is valid, False otherwise
-
"""
- if password is None:
- return False
- if len(password) < 8:
- return False
- if re.match(r"[A-Z]", password) is None:
- return False
- if re.match(r"[a-z]", password) is None:
- return False
- if re.match(r"[0-9]", password) is None:
+ if not password or len(password) < 8:
return False
- return re.match("[^A-Za-z0-9]", password) is not None
+ return bool(re.match(r"^(?=.*[A-Z])(?=.*[a-z])(?=.*\d)(?=.*[^A-Za-z0-9]).{8,}$", password))
diff --git a/api/src/utils/ipv6.py b/api/src/utils/ipv6.py
index e2b1d49..773f9e1 100644
--- a/api/src/utils/ipv6.py
+++ b/api/src/utils/ipv6.py
@@ -1,5 +1,4 @@
"""Module for IPv6 utilities."""
-
def simplify(ipv6: str) -> str:
"""Simplify an IPv6 address.
@@ -10,28 +9,44 @@ def simplify(ipv6: str) -> str:
str: The simplified IPv6 address.
"""
- parts = ipv6.split(":")
- for part in parts:
- if part == "0000":
- parts[parts.index(part)] = "0"
-
- is_zero = False
- if ipv6.contains("::"):
- is_zero = True
- i = 0
- while i < len(parts):
- part = parts[i]
- if part == "0":
- parts.pop(i)
- is_zero = True
- elif is_zero:
- parts.insert(i, "")
- i = len(parts) # break
+ # Split the address into its groups
+ groups = ipv6.split(":")
+
+ # Step 1: Remove leading zeros in each group
+ groups = [group.lstrip("0") or "0" for group in groups]
+
+ # Step 2: Identify the longest run of consecutive "0" groups
+ zero_runs = []
+ current_start = None
+ for i, group in enumerate(groups):
+ if group == "0":
+ if current_start is None:
+ current_start = i
else:
- i += 1
-
- return ":".join(parts)
-
+ if current_start is not None:
+ zero_runs.append((current_start, i - 1))
+ current_start = None
+ if current_start is not None:
+ zero_runs.append((current_start, len(groups) - 1))
+
+ # Find the longest run of zeros
+ if zero_runs:
+ longest_run = max(zero_runs, key=lambda run: run[1] - run[0])
+ else:
+ longest_run = None
+
+ # Step 3: Replace the longest run of zeros with "::"
+ if longest_run:
+ start, end = longest_run
+ groups = groups[:start] + [""] + groups[end + 1:]
+
+ # Step 4: Reconstruct the compressed address
+ compressed_address = ":".join(groups)
+
+ # Ensure "::" is not accidentally replaced multiple times
+ compressed_address = compressed_address.replace(":::", "::")
+
+ return compressed_address
def extend(ipv6: str) -> str:
"""Extend an IPv6 address.
@@ -42,18 +57,23 @@ def extend(ipv6: str) -> str:
str: The extended IPv6 address.
"""
- parts = ipv6.split(":")
- i = 0
- while i < len(parts):
- if parts[i] == "":
- parts[i] = "0000"
- for _j in range(8 - len(parts)):
- parts.insert(i, "0000")
- i += 8 - len(parts)
- elif parts[i] == "0":
- parts[i] = "0000"
- i += 1
- else:
- i += 1
-
- return ":".join(parts)
+ if "::" in ipv6:
+ parts = ipv6.split("::")
+ left = parts[0].split(":") if parts[0] else []
+ right = parts[1].split(":") if len(parts) > 1 and parts[1] else []
+
+ # Calculate the number of groups needed to fill the gap
+ num_zeros_to_add = 8 - (len(left) + len(right))
+ middle = ["0000"] * num_zeros_to_add
+
+ # Combine all parts into a full address
+ full_address = left + middle + right
+ else:
+ # If no "::", simply split by ":" for expansion
+ full_address = ipv6.split(":")
+
+ # Step 2: Pad each group to 4 digits
+ expanded_address = [group.zfill(4) for group in full_address]
+
+ # Step 3: Join the groups with ":"
+ return ":".join(expanded_address)
diff --git a/front-js/package-lock.json b/front-js/package-lock.json
index c236a25..f74cf3d 100644
--- a/front-js/package-lock.json
+++ b/front-js/package-lock.json
@@ -17,6 +17,7 @@
"next": "15.1.0",
"react": "^19.0.0",
"react-apple-emojis": "^2.2.2",
+ "react-code-blocks": "^0.1.6",
"react-dom": "^19.0.0",
"sass": "^1.83.0"
},
@@ -1704,6 +1705,14 @@
"integrity": "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==",
"dev": true
},
+ "node_modules/@types/hast": {
+ "version": "2.3.10",
+ "resolved": "https://registry.npmjs.org/@types/hast/-/hast-2.3.10.tgz",
+ "integrity": "sha512-McWspRw8xx8J9HurkVBfYj0xKoE25tOFlHGdx4MJ5xORQrMGZNqJhVQWaIbm6Oyla5kYOXtDiopzKRJzEOkwJw==",
+ "dependencies": {
+ "@types/unist": "^2"
+ }
+ },
"node_modules/@types/js-cookie": {
"version": "3.0.6",
"resolved": "https://registry.npmjs.org/@types/js-cookie/-/js-cookie-3.0.6.tgz",
@@ -1766,6 +1775,16 @@
"@types/react": "*"
}
},
+ "node_modules/@types/stylis": {
+ "version": "4.2.5",
+ "resolved": "https://registry.npmjs.org/@types/stylis/-/stylis-4.2.5.tgz",
+ "integrity": "sha512-1Xve+NMN7FWjY14vLoY5tL3BVEQ/n42YLwaqJIPYhotZ9uBHt87VceMwWQpzmdEt2TNXIorIFG+YeCUUW7RInw=="
+ },
+ "node_modules/@types/unist": {
+ "version": "2.0.11",
+ "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.11.tgz",
+ "integrity": "sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA=="
+ },
"node_modules/@typescript-eslint/eslint-plugin": {
"version": "8.18.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.18.1.tgz",
@@ -2423,6 +2442,14 @@
"node": ">= 6"
}
},
+ "node_modules/camelize": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/camelize/-/camelize-1.0.1.tgz",
+ "integrity": "sha512-dU+Tx2fsypxTgtLoE36npi3UqcjSSMNYfkqgmoEhtZrraP5VWq0K7FkWVTYa8eMPtnU/G2txVsfdCJTn9uzpuQ==",
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
"node_modules/caniuse-lite": {
"version": "1.0.30001689",
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001689.tgz",
@@ -2458,6 +2485,33 @@
"url": "https://github.com/chalk/chalk?sponsor=1"
}
},
+ "node_modules/character-entities": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/character-entities/-/character-entities-1.2.4.tgz",
+ "integrity": "sha512-iBMyeEHxfVnIakwOuDXpVkc54HijNgCyQB2w0VfGQThle6NXn50zU6V/u+LDhxHcDUPojn6Kpga3PTAD8W1bQw==",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/character-entities-legacy": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/character-entities-legacy/-/character-entities-legacy-1.1.4.tgz",
+ "integrity": "sha512-3Xnr+7ZFS1uxeiUDvV02wQ+QDbc55o97tIV5zHScSPJpcLm/r0DFPcoY3tYRp+VZukxuMeKgXYmsXQHO05zQeA==",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/character-reference-invalid": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/character-reference-invalid/-/character-reference-invalid-1.1.4.tgz",
+ "integrity": "sha512-mKKUkUbhPpQlCOfIuZkvSEgktjPFIsZKRRbC6KWVEMvlzblj3i3asQv5ODsrwt0N3pHAEvjP8KTQPHkp0+6jOg==",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
"node_modules/chokidar": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.2.tgz",
@@ -2539,6 +2593,15 @@
"node": ">= 0.8"
}
},
+ "node_modules/comma-separated-tokens": {
+ "version": "1.0.8",
+ "resolved": "https://registry.npmjs.org/comma-separated-tokens/-/comma-separated-tokens-1.0.8.tgz",
+ "integrity": "sha512-GHuDRO12Sypu2cV70d1dkA2EUmXHgntrzbpvOB+Qy+49ypNfGgFQIC2fhhXbnyrJRynDCAARsT7Ou0M6hirpfw==",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
"node_modules/commander": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz",
@@ -2587,6 +2650,24 @@
"node": ">= 8"
}
},
+ "node_modules/css-color-keywords": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/css-color-keywords/-/css-color-keywords-1.0.0.tgz",
+ "integrity": "sha512-FyyrDHZKEjXDpNJYvVsV960FiqQyXc/LlYmsxl2BcdMb2WPx0OGRVgTg55rPSyLSNMqP52R9r8geSp7apN3Ofg==",
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/css-to-react-native": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/css-to-react-native/-/css-to-react-native-3.2.0.tgz",
+ "integrity": "sha512-e8RKaLXMOFii+02mOlqwjbD00KSEKqblnpO9e++1aXS1fPQOpS1YoqdVHBqPjHNoxeF2mimzVqawm2KCbEdtHQ==",
+ "dependencies": {
+ "camelize": "^1.0.0",
+ "css-color-keywords": "^1.0.0",
+ "postcss-value-parser": "^4.0.2"
+ }
+ },
"node_modules/cssesc": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz",
@@ -3492,6 +3573,18 @@
"reusify": "^1.0.4"
}
},
+ "node_modules/fault": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/fault/-/fault-1.0.4.tgz",
+ "integrity": "sha512-CJ0HCB5tL5fYTEA7ToAq5+kTwd++Borf1/bifxd9iT70QcXr4MRrO3Llf8Ifs70q+SJcGHFtnIE/Nw6giCtECA==",
+ "dependencies": {
+ "format": "^0.2.0"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
"node_modules/file-entry-cache": {
"version": "8.0.0",
"resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz",
@@ -3621,6 +3714,14 @@
"node": ">= 6"
}
},
+ "node_modules/format": {
+ "version": "0.2.2",
+ "resolved": "https://registry.npmjs.org/format/-/format-0.2.2.tgz",
+ "integrity": "sha512-wzsgA6WOq+09wrU1tsJ09udeR/YZRaeArL9e1wPbFg3GG2yDnC2ldKpxs4xunpFF9DgqCqOIra3bc1HWrJ37Ww==",
+ "engines": {
+ "node": ">=0.4.x"
+ }
+ },
"node_modules/fsevents": {
"version": "2.3.3",
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
@@ -3913,6 +4014,44 @@
"node": ">= 0.4"
}
},
+ "node_modules/hast-util-parse-selector": {
+ "version": "2.2.5",
+ "resolved": "https://registry.npmjs.org/hast-util-parse-selector/-/hast-util-parse-selector-2.2.5.tgz",
+ "integrity": "sha512-7j6mrk/qqkSehsM92wQjdIgWM2/BW61u/53G6xmC8i1OmEdKLHbk419QKQUjz6LglWsfqoiHmyMRkP1BGjecNQ==",
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/hastscript": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/hastscript/-/hastscript-6.0.0.tgz",
+ "integrity": "sha512-nDM6bvd7lIqDUiYEiu5Sl/+6ReP0BMk/2f4U/Rooccxkj0P5nm+acM5PrGJ/t5I8qPGiqZSE6hVAwZEdZIvP4w==",
+ "dependencies": {
+ "@types/hast": "^2.0.0",
+ "comma-separated-tokens": "^1.0.0",
+ "hast-util-parse-selector": "^2.0.0",
+ "property-information": "^5.0.0",
+ "space-separated-tokens": "^1.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/highlight.js": {
+ "version": "10.7.3",
+ "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-10.7.3.tgz",
+ "integrity": "sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A==",
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/highlightjs-vue": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/highlightjs-vue/-/highlightjs-vue-1.0.0.tgz",
+ "integrity": "sha512-PDEfEF102G23vHmPhLyPboFCD+BkMGu+GuJe2d9/eH4FsCwvgBpnc9n0pGE+ffKdph38s6foEZiEjdgHdzp+IA=="
+ },
"node_modules/hoist-non-react-statics": {
"version": "3.3.2",
"resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz",
@@ -3978,6 +4117,28 @@
"node": ">= 0.4"
}
},
+ "node_modules/is-alphabetical": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/is-alphabetical/-/is-alphabetical-1.0.4.tgz",
+ "integrity": "sha512-DwzsA04LQ10FHTZuL0/grVDk4rFoVH1pjAToYwBrHSxcrBIGQuXrQMtD5U1b0U2XVgKZCTLLP8u2Qxqhy3l2Vg==",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/is-alphanumerical": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/is-alphanumerical/-/is-alphanumerical-1.0.4.tgz",
+ "integrity": "sha512-UzoZUr+XfVz3t3v4KyGEniVL9BDRoQtY7tOyrRybkVNjDFWyo1yhXNGrrBTQxp3ib9BLAWs7k2YKBQsFRkZG9A==",
+ "dependencies": {
+ "is-alphabetical": "^1.0.0",
+ "is-decimal": "^1.0.0"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
"node_modules/is-array-buffer": {
"version": "3.0.5",
"resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.5.tgz",
@@ -4126,6 +4287,15 @@
"url": "https://github.com/sponsors/ljharb"
}
},
+ "node_modules/is-decimal": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/is-decimal/-/is-decimal-1.0.4.tgz",
+ "integrity": "sha512-RGdriMmQQvZ2aqaQq3awNA6dCGtKpiDFcOzrTWrDAT2MiWrKQVPmxLGHl7Y2nNu6led0kEyoX0enY0qXYsv9zw==",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
"node_modules/is-extglob": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
@@ -4184,6 +4354,15 @@
"node": ">=0.10.0"
}
},
+ "node_modules/is-hexadecimal": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/is-hexadecimal/-/is-hexadecimal-1.0.4.tgz",
+ "integrity": "sha512-gyPJuv83bHMpocVYoqof5VDiZveEoGoFL8m3BXNb2VW8Xs+rz9kqO8LOQ5DH6EsuvilT1ApazU0pyl+ytbPtlw==",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
"node_modules/is-map": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.3.tgz",
@@ -4595,6 +4774,19 @@
"loose-envify": "cli.js"
}
},
+ "node_modules/lowlight": {
+ "version": "1.20.0",
+ "resolved": "https://registry.npmjs.org/lowlight/-/lowlight-1.20.0.tgz",
+ "integrity": "sha512-8Ktj+prEb1RoCPkEOrPMYUN/nCggB7qAWe3a7OpMjWQkh3l2RD5wKRQ+o8Q8YuI9RG/xs95waaI/E6ym/7NsTw==",
+ "dependencies": {
+ "fault": "^1.0.0",
+ "highlight.js": "~10.7.0"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
"node_modules/lru-cache": {
"version": "10.4.3",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz",
@@ -4969,6 +5161,23 @@
"node": ">=6"
}
},
+ "node_modules/parse-entities": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/parse-entities/-/parse-entities-2.0.0.tgz",
+ "integrity": "sha512-kkywGpCcRYhqQIchaWqZ875wzpS/bMKhz5HnN3p7wveJTkTtyAB/AlnS0f8DFSqYW1T82t6yEAkEcB+A1I3MbQ==",
+ "dependencies": {
+ "character-entities": "^1.0.0",
+ "character-entities-legacy": "^1.0.0",
+ "character-reference-invalid": "^1.0.0",
+ "is-alphanumerical": "^1.0.0",
+ "is-decimal": "^1.0.0",
+ "is-hexadecimal": "^1.0.0"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
"node_modules/parse-json": {
"version": "5.2.0",
"resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz",
@@ -5226,8 +5435,7 @@
"node_modules/postcss-value-parser": {
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz",
- "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==",
- "peer": true
+ "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ=="
},
"node_modules/prelude-ls": {
"version": "1.2.1",
@@ -5253,6 +5461,14 @@
"url": "https://github.com/prettier/prettier?sponsor=1"
}
},
+ "node_modules/prismjs": {
+ "version": "1.29.0",
+ "resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.29.0.tgz",
+ "integrity": "sha512-Kx/1w86q/epKcmte75LNrEoT+lX8pBpavuAbvJWRXar7Hz8jrtF+e3vY751p0R8H9HdArwaCTNDDzHg/ScJK1Q==",
+ "engines": {
+ "node": ">=6"
+ }
+ },
"node_modules/prop-types": {
"version": "15.8.1",
"resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz",
@@ -5268,6 +5484,18 @@
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
"integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="
},
+ "node_modules/property-information": {
+ "version": "5.6.0",
+ "resolved": "https://registry.npmjs.org/property-information/-/property-information-5.6.0.tgz",
+ "integrity": "sha512-YUHSPk+A30YPv+0Qf8i9Mbfe/C0hdPXk1s1jPVToV8pk8BQtpw10ct89Eo7OWkutrwqvT0eicAxlOg3dOAu8JA==",
+ "dependencies": {
+ "xtend": "^4.0.0"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
"node_modules/proxy-from-env": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
@@ -5319,6 +5547,23 @@
"react-dom": ">=16.x"
}
},
+ "node_modules/react-code-blocks": {
+ "version": "0.1.6",
+ "resolved": "https://registry.npmjs.org/react-code-blocks/-/react-code-blocks-0.1.6.tgz",
+ "integrity": "sha512-ENNuxG07yO+OuX1ChRje3ieefPRz6yrIpHmebQlaFQgzcAHbUfVeTINpOpoI9bSRSObeYo/OdHsporeToZ7fcg==",
+ "dependencies": {
+ "@babel/runtime": "^7.10.4",
+ "react-syntax-highlighter": "^15.5.0",
+ "styled-components": "^6.1.0",
+ "tslib": "^2.6.0"
+ },
+ "engines": {
+ "node": ">=16"
+ },
+ "peerDependencies": {
+ "react": ">=16"
+ }
+ },
"node_modules/react-dom": {
"version": "19.0.0",
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.0.0.tgz",
@@ -5335,6 +5580,22 @@
"resolved": "https://registry.npmjs.org/react-is/-/react-is-19.0.0.tgz",
"integrity": "sha512-H91OHcwjZsbq3ClIDHMzBShc1rotbfACdWENsmEf0IFvZ3FgGPtdHMcsv45bQ1hAbgdfiA8SnxTKfDS+x/8m2g=="
},
+ "node_modules/react-syntax-highlighter": {
+ "version": "15.6.1",
+ "resolved": "https://registry.npmjs.org/react-syntax-highlighter/-/react-syntax-highlighter-15.6.1.tgz",
+ "integrity": "sha512-OqJ2/vL7lEeV5zTJyG7kmARppUjiB9h9udl4qHQjjgEos66z00Ia0OckwYfRxCSFrW8RJIBnsBwQsHZbVPspqg==",
+ "dependencies": {
+ "@babel/runtime": "^7.3.1",
+ "highlight.js": "^10.4.1",
+ "highlightjs-vue": "^1.0.0",
+ "lowlight": "^1.17.0",
+ "prismjs": "^1.27.0",
+ "refractor": "^3.6.0"
+ },
+ "peerDependencies": {
+ "react": ">= 0.14.0"
+ }
+ },
"node_modules/react-transition-group": {
"version": "4.4.5",
"resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.5.tgz",
@@ -5393,6 +5654,28 @@
"url": "https://github.com/sponsors/ljharb"
}
},
+ "node_modules/refractor": {
+ "version": "3.6.0",
+ "resolved": "https://registry.npmjs.org/refractor/-/refractor-3.6.0.tgz",
+ "integrity": "sha512-MY9W41IOWxxk31o+YvFCNyNzdkc9M20NoZK5vq6jkv4I/uh2zkWcfudj0Q1fovjUQJrNewS9NMzeTtqPf+n5EA==",
+ "dependencies": {
+ "hastscript": "^6.0.0",
+ "parse-entities": "^2.0.0",
+ "prismjs": "~1.27.0"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/refractor/node_modules/prismjs": {
+ "version": "1.27.0",
+ "resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.27.0.tgz",
+ "integrity": "sha512-t13BGPUlFDR7wRB5kQDG4jjl7XeuH6jbJGt11JHPL96qwsEHNX2+68tFXqc1/k+/jALsbSWJKUOT/hcYAZ5LkA==",
+ "engines": {
+ "node": ">=6"
+ }
+ },
"node_modules/regenerator-runtime": {
"version": "0.14.1",
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz",
@@ -5584,6 +5867,11 @@
"node": ">= 0.4"
}
},
+ "node_modules/shallowequal": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/shallowequal/-/shallowequal-1.1.0.tgz",
+ "integrity": "sha512-y0m1JoUZSlPAjXVtPPW70aZWfIL/dSP7AFkRnniLCrK/8MDKog3TySTBmckD+RObVxH0v4Tox67+F14PdED2oQ=="
+ },
"node_modules/sharp": {
"version": "0.33.5",
"resolved": "https://registry.npmjs.org/sharp/-/sharp-0.33.5.tgz",
@@ -5764,6 +6052,15 @@
"node": ">=0.10.0"
}
},
+ "node_modules/space-separated-tokens": {
+ "version": "1.1.5",
+ "resolved": "https://registry.npmjs.org/space-separated-tokens/-/space-separated-tokens-1.1.5.tgz",
+ "integrity": "sha512-q/JSVd1Lptzhf5bkYm4ob4iWPjx0KiRe3sRFBNrVqbJkFaBm5vbbowy1mymoPNLRa52+oadOhJ+K49wsSeSjTA==",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
"node_modules/stable-hash": {
"version": "0.0.4",
"resolved": "https://registry.npmjs.org/stable-hash/-/stable-hash-0.0.4.tgz",
@@ -6001,6 +6298,88 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
+ "node_modules/styled-components": {
+ "version": "6.1.13",
+ "resolved": "https://registry.npmjs.org/styled-components/-/styled-components-6.1.13.tgz",
+ "integrity": "sha512-M0+N2xSnAtwcVAQeFEsGWFFxXDftHUD7XrKla06QbpUMmbmtFBMMTcKWvFXtWxuD5qQkB8iU5gk6QASlx2ZRMw==",
+ "dependencies": {
+ "@emotion/is-prop-valid": "1.2.2",
+ "@emotion/unitless": "0.8.1",
+ "@types/stylis": "4.2.5",
+ "css-to-react-native": "3.2.0",
+ "csstype": "3.1.3",
+ "postcss": "8.4.38",
+ "shallowequal": "1.1.0",
+ "stylis": "4.3.2",
+ "tslib": "2.6.2"
+ },
+ "engines": {
+ "node": ">= 16"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/styled-components"
+ },
+ "peerDependencies": {
+ "react": ">= 16.8.0",
+ "react-dom": ">= 16.8.0"
+ }
+ },
+ "node_modules/styled-components/node_modules/@emotion/is-prop-valid": {
+ "version": "1.2.2",
+ "resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-1.2.2.tgz",
+ "integrity": "sha512-uNsoYd37AFmaCdXlg6EYD1KaPOaRWRByMCYzbKUX4+hhMfrxdVSelShywL4JVaAeM/eHUOSprYBQls+/neX3pw==",
+ "dependencies": {
+ "@emotion/memoize": "^0.8.1"
+ }
+ },
+ "node_modules/styled-components/node_modules/@emotion/memoize": {
+ "version": "0.8.1",
+ "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.8.1.tgz",
+ "integrity": "sha512-W2P2c/VRW1/1tLox0mVUalvnWXxavmv/Oum2aPsRcoDJuob75FC3Y8FbpfLwUegRcxINtGUMPq0tFCvYNTBXNA=="
+ },
+ "node_modules/styled-components/node_modules/@emotion/unitless": {
+ "version": "0.8.1",
+ "resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.8.1.tgz",
+ "integrity": "sha512-KOEGMu6dmJZtpadb476IsZBclKvILjopjUii3V+7MnXIQCYh8W3NgNcgwo21n9LXZX6EDIKvqfjYxXebDwxKmQ=="
+ },
+ "node_modules/styled-components/node_modules/postcss": {
+ "version": "8.4.38",
+ "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.38.tgz",
+ "integrity": "sha512-Wglpdk03BSfXkHoQa3b/oulrotAkwrlLDRSOb9D0bN86FdRyE9lppSp33aHNPgBa0JKCoB+drFLZkQoRRYae5A==",
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/postcss/"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/postcss"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "dependencies": {
+ "nanoid": "^3.3.7",
+ "picocolors": "^1.0.0",
+ "source-map-js": "^1.2.0"
+ },
+ "engines": {
+ "node": "^10 || ^12 || >=14"
+ }
+ },
+ "node_modules/styled-components/node_modules/stylis": {
+ "version": "4.3.2",
+ "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.3.2.tgz",
+ "integrity": "sha512-bhtUjWd/z6ltJiQwg0dUfxEJ+W+jdqQd8TbWLWyeIJHlnsqmGLRFFd8e5mA0AZi/zx90smXRlN66YMTcaSFifg=="
+ },
+ "node_modules/styled-components/node_modules/tslib": {
+ "version": "2.6.2",
+ "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz",
+ "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q=="
+ },
"node_modules/styled-jsx": {
"version": "5.1.6",
"resolved": "https://registry.npmjs.org/styled-jsx/-/styled-jsx-5.1.6.tgz",
@@ -6659,6 +7038,14 @@
"url": "https://github.com/chalk/ansi-styles?sponsor=1"
}
},
+ "node_modules/xtend": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz",
+ "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==",
+ "engines": {
+ "node": ">=0.4"
+ }
+ },
"node_modules/yaml": {
"version": "1.10.2",
"resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz",
diff --git a/front-js/package.json b/front-js/package.json
index 7a23fef..e322484 100644
--- a/front-js/package.json
+++ b/front-js/package.json
@@ -19,6 +19,7 @@
"next": "15.1.0",
"react": "^19.0.0",
"react-apple-emojis": "^2.2.2",
+ "react-code-blocks": "^0.1.6",
"react-dom": "^19.0.0",
"sass": "^1.83.0"
},
diff --git a/front-js/src/app/auth/signup/page.tsx b/front-js/src/app/auth/signup/page.tsx
index 9836f29..3e8d599 100644
--- a/front-js/src/app/auth/signup/page.tsx
+++ b/front-js/src/app/auth/signup/page.tsx
@@ -44,7 +44,14 @@ export default function Home() {
} catch (error: unknown) {
const axiosError = error as AxiosError;
if (axiosError.response?.status === 400) {
- setError("Nom d'utilisateur déjà utilisé");
+ const data = axiosError.response.data as { detail: string };
+ if (data.detail === "User already exists") {
+ setError("Nom d'utilisateur déjà utilisé");
+ } else if (data.detail == "Invalid password") {
+ setError("Mot de passe invalide");
+ } else {
+ setError("Erreur lors de l'inscription");
+ }
} else {
setError("Erreur lors de l'inscription");
}
diff --git a/front-js/src/app/dashboard/page.tsx b/front-js/src/app/dashboard/page.tsx
index 711bbbf..5d4c826 100644
--- a/front-js/src/app/dashboard/page.tsx
+++ b/front-js/src/app/dashboard/page.tsx
@@ -93,7 +93,7 @@ export default function Dashboard() {
) : (
<>
-
+
Bonjour {username}
@@ -114,6 +114,7 @@ export default function Dashboard() {
title="IPv6"
description="Simplifiez ou étendez une adresse IPv6 avec ce module."
image="/modules_assets/ipv6.svg"
+ onClick={() => router.push("/modules/ipv6")}
/>
diff --git a/front-js/src/app/modules/ipv6/Cours.tsx b/front-js/src/app/modules/ipv6/Cours.tsx
new file mode 100644
index 0000000..aaeead7
--- /dev/null
+++ b/front-js/src/app/modules/ipv6/Cours.tsx
@@ -0,0 +1,185 @@
+import Title from "@/components/Title";
+import Text from "@/components/Text";
+import React from "react";
+import { Code, solarizedLight } from "react-code-blocks";
+import { Spacer } from "@nextui-org/spacer";
+import Space from "@/components/Space";
+import { Emoji, EmojiProvider } from "react-apple-emojis";
+import emojiData from "react-apple-emojis/src/data.json";
+import Input from "@/components/Input";
+import { useState } from "react";
+import axios from "@/axiosConfig";
+import Cookies from "js-cookie";
+import { AxiosError } from "axios";
+import Button from "@/components/Button";
+import { Alert } from "@mui/material";
+import Box from "@/components/Box";
+
+const Cours: React.FC = () => {
+ const [ipv6test, setIPv6test] = useState("");
+ const [res, setRes] = useState("");
+ const [valid, setValid] = useState(false);
+
+ const handleSimplify = async (e: { preventDefault: () => void }) => {
+ e.preventDefault();
+ try {
+ const response = await axios.get(
+ "/ipv6/simplify/fe80:0000:0000:0000:0202:b3ff:fe1e:8329",
+ {
+ headers: {
+ Authorization: `Bearer ${Cookies.get("access_token")}`,
+ },
+ }
+ );
+ const data = response.data;
+ if (response.status === 200) {
+ setRes(data.ipv6);
+ setValid(res == ipv6test);
+ }
+ } catch (error: unknown) {
+ const axiosError = error as AxiosError;
+ if (axiosError.response?.status === 400) {
+ const data = axiosError.response.data as { detail: string };
+ if (data.detail === "Invalid IPv6") {
+ setRes("false");
+ } else {
+ setRes("false");
+ }
+ } else {
+ setRes("false");
+ }
+ }
+ };
+
+ return (
+ <>
+
+
+ Introduction
+
+ IPv6 (Internet Protocol version 6) est une version améliorée
+ d’IPv4, conçue pour répondre à la pénurie d’adresses IP
+ due à l’explosion des appareils connectés. Alors qu’IPv4 offre
+ environ 4,3 milliards d’adresses, IPv6 permet d’en générer 340
+ sextillions, garantissant ainsi un espace suffisant pour l’avenir.
+
+
+ Structure IPv6
+
+
+ IPv6 est composé de 128 bits, contre 32 bits pour IPv4. Cela permet
+ de créer un nombre d’adresses IP bien plus important.
+ L’IPv6 est représenté sous la forme de huit groupes de quatre
+ chiffres hexadécimaux séparés par des deux-points. Exemple :
+
+
+
+
+ Simplification d’une adresse IPv6
+
+
+
+ La simplification d’une adresse IPv6 se fait en 2 étapes.
+ Prenons une adresse IPv6 complète :
+
+
+ 1. Suppression des zéros initiaux dans chaque groupe :{" "}
+
+
+ -
+ {" "}
+ devient{" "}
+ {" "}
+
+
+ -
+ {" "}
+ devient{" "}
+ {" "}
+
+
+ Résultat intermédiaire :{" "}
+ {" "}
+
+
+ 2. Remplacement des groupes consécutifs de{" "}
+ par{" "}
+ :{" "}
+
+
+ - Une séquence continue de groupes{" "}
+ peut
+ être remplacée par{" "}
+ (une
+ seule fois par adresse).{" "}
+
+
+ -Résultat final :{" "}
+
+
+
+
+
+ Essayez !
+
+ Simplifiez cette adresse IPv6 en remplaçant les groupes de zéro
+
+
+
+ {res &&
+ (valid ? (
+
+ Bravo !
+
+ ) : (
+
+ Raté !
+
+ ))}
+
+
+
+ >
+ );
+};
+
+export default Cours;
diff --git a/front-js/src/app/modules/ipv6/IpSandbox.tsx b/front-js/src/app/modules/ipv6/IpSandbox.tsx
new file mode 100644
index 0000000..02bbdf1
--- /dev/null
+++ b/front-js/src/app/modules/ipv6/IpSandbox.tsx
@@ -0,0 +1,153 @@
+import Title from "@/components/Title";
+import Button from "@/components/Button";
+import Input from "@/components/Input";
+import Space from "@/components/Space";
+import React from "react";
+import { useState } from "react";
+import axios from "@/axiosConfig";
+import Cookies from "js-cookie";
+import { AxiosError } from "axios";
+import { Alert } from "@mui/material";
+import Box from "@/components/Box";
+
+const IpSandbox: React.FC = () => {
+ const [simpleIpv6, setSimpleIpv6] = useState("");
+ const [simpleRes, setSimpleRes] = useState("");
+
+ const handleSimplify = async (e: { preventDefault: () => void }) => {
+ e.preventDefault();
+ try {
+ const response = await axios.get("/ipv6/simplify/ " + simpleIpv6, {
+ headers: {
+ Authorization: `Bearer ${Cookies.get("access_token")}`,
+ },
+ });
+ const data = response.data;
+ if (response.status === 200) {
+ setSimpleRes(data.ipv6);
+ }
+ } catch (error: unknown) {
+ const axiosError = error as AxiosError;
+ if (axiosError.response?.status === 400) {
+ const data = axiosError.response.data as { detail: string };
+ if (data.detail === "Invalid IPv6") {
+ setSimpleRes("Adresse IPv6 invalide");
+ } else {
+ setSimpleRes("Adresse IPv6 invalide");
+ }
+ } else {
+ setSimpleRes("Erreur lors de la simplification de l'adresse IPv6");
+ }
+ }
+ };
+
+ const [extendIpv6, setExtendIpv6] = useState("");
+ const [extendRes, setExtendRes] = useState("");
+
+ const handleExtend = async (e: { preventDefault: () => void }) => {
+ e.preventDefault();
+ try {
+ const response = await axios.get("/ipv6/extend/" + extendIpv6, {
+ headers: {
+ Authorization: `Bearer ${Cookies.get("access_token")}`,
+ },
+ });
+ const data = response.data;
+ if (response.status === 200) {
+ setExtendRes(data.ipv6);
+ }
+ } catch (error: unknown) {
+ const axiosError = error as AxiosError;
+ if (axiosError.response?.status === 400) {
+ const data = axiosError.response.data as { detail: string };
+ if (data.detail === "Invalid IPv6") {
+ setExtendRes("Adresse IPv6 invalide");
+ } else {
+ setExtendRes("Adresse IPv6 invalide");
+ }
+ } else {
+ setExtendRes("Erreur lors de l'extension de l'adresse IPv6");
+ }
+ }
+ };
+
+ return (
+ <>
+
+
+
+
+
+ >
+ );
+};
+
+export default IpSandbox;
diff --git a/front-js/src/app/modules/ipv6/layout.tsx b/front-js/src/app/modules/ipv6/layout.tsx
new file mode 100644
index 0000000..d476975
--- /dev/null
+++ b/front-js/src/app/modules/ipv6/layout.tsx
@@ -0,0 +1,14 @@
+import type { Metadata } from "next";
+
+export const metadata: Metadata = {
+ title: "IPv6",
+ description: "Networkers",
+};
+
+export default function RootLayout({
+ children,
+}: Readonly<{
+ children: React.ReactNode;
+}>) {
+ return {children};
+}
diff --git a/front-js/src/app/modules/ipv6/page.tsx b/front-js/src/app/modules/ipv6/page.tsx
index e69de29..977b1c8 100644
--- a/front-js/src/app/modules/ipv6/page.tsx
+++ b/front-js/src/app/modules/ipv6/page.tsx
@@ -0,0 +1,90 @@
+"use client";
+
+import Layout from "@/components/Layout";
+import Header from "@/components/Header";
+import Title from "@/components/Title";
+import Box from "@/components/Box";
+import { useRouter } from "next/navigation";
+import { useEffect, useState } from "react";
+import Cookies from "js-cookie";
+import axios from "@/axiosConfig";
+import { Tabs, Tab } from "@mui/material";
+import Cours from "./Cours";
+import IpSandbox from "./IpSandbox";
+
+export default function Dashboard() {
+ const router = useRouter();
+
+ const [, setHasAccessToken] = useState(false);
+
+ useEffect(() => {
+ const checkTokens = async () => {
+ const token = Cookies.get("access_token");
+ const refresh = Cookies.get("refresh_token");
+ if (!token && refresh) {
+ try {
+ const response = await axios.post("/auth/refresh", {
+ refresh_token: refresh,
+ });
+ const data = response.data;
+ if (response.status === 200) {
+ Cookies.set("access_token", data.access_token);
+ Cookies.set("refresh_token", refresh);
+ }
+ } catch {
+ Cookies.remove("access_token");
+ Cookies.remove("refresh_token");
+ }
+ }
+ setHasAccessToken(!!token);
+
+ if (!token && !refresh) {
+ router.push("/");
+ }
+ };
+
+ checkTokens();
+ }, [router]);
+
+ const [activeTab, setActiveTab] = useState("cours");
+
+ const handleTabChange = (tab: string) => {
+ setActiveTab(tab);
+ };
+
+ return (
+
+
+ router.push(`/${tab.toLowerCase()}`)}
+ onClickLogout={() => router.push("/auth/logout")}
+ onClickLogo={() => router.push("/")}
+ />
+
+
+
+ IPv6
+
+
+
+ handleTabChange(tab)}
+ centered
+ >
+
+
+
+
+
+ {activeTab === "cours" ? : }
+
+
+ );
+}
diff --git a/front-js/src/components/Text/Text.tsx b/front-js/src/components/Text/Text.tsx
index 9d7730c..5ce1709 100644
--- a/front-js/src/components/Text/Text.tsx
+++ b/front-js/src/components/Text/Text.tsx
@@ -18,6 +18,7 @@ interface TextProps {
className?: string;
icon?: React.ReactNode;
iconPosition?: "start" | "end"; // Add the iconPosition prop here
+ isItalic?: boolean;
}
const Text: React.FC = ({
@@ -30,6 +31,7 @@ const Text: React.FC = ({
className,
icon,
iconPosition = "start", // Destructure the iconPosition prop here with a default value
+ isItalic,
}) => {
return (
= ({
marginTop: margin?.top,
marginBottom: margin?.bottom,
textAlign: align,
+ fontStyle: isItalic ? "italic" : "normal",
}}
>
{iconPosition === "start" && icon && {icon}}
diff --git a/front-js/src/utils/validatePasswd.js b/front-js/src/utils/validatePasswd.js
index a9fd348..4bdf352 100644
--- a/front-js/src/utils/validatePasswd.js
+++ b/front-js/src/utils/validatePasswd.js
@@ -1,23 +1,6 @@
export function validate_passwd(passwd) {
- if (passwd.length < 8) {
+ if (!passwd.match(/^(?=.*[A-Z])(?=.*[a-z])(?=.*\d)(?=.*[^A-Za-z0-9]).{8,}$/)) {
return false;
}
-
- if (!/[0-9]/.test(passwd)) {
- return false;
- }
-
- if (!/[a-z]/.test(passwd)) {
- return false;
- }
-
- if (!/[A-Z]/.test(passwd)) {
- return false;
- }
-
- if (!/[^A-Za-z0-9]/.test(passwd)) {
- return false;
- }
-
return true;
}
\ No newline at end of file