diff --git a/.copier/update_dotenv.py b/.copier/update_dotenv.py index 657688562..35aa854b1 100644 --- a/.copier/update_dotenv.py +++ b/.copier/update_dotenv.py @@ -1,5 +1,5 @@ -from pathlib import Path import json +from pathlib import Path # Update the .env file with the answers from the .copier-answers.yml file # without using Jinja2 templates in the .env file, this way the code works as is diff --git a/backend/Dockerfile b/backend/Dockerfile index 663d2b907..f3d13d019 100644 --- a/backend/Dockerfile +++ b/backend/Dockerfile @@ -42,4 +42,3 @@ RUN --mount=type=cache,target=/root/.cache/uv \ # CMD ["fastapi", "run", "--workers", "1", "app/main.py"] CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000", "--workers", "1", "--proxy-headers", "--forwarded-allow-ips", "*"] - diff --git a/backend/app/api/main.py b/backend/app/api/main.py index 80e64ac7d..5eee7d016 100644 --- a/backend/app/api/main.py +++ b/backend/app/api/main.py @@ -1,6 +1,6 @@ from fastapi import APIRouter -from app.api.routes import items, login, private, users, utils, ip +from app.api.routes import ip, items, login, private, users, utils from app.core.config import settings api_router = APIRouter() diff --git a/backend/app/api/routes/ip.py b/backend/app/api/routes/ip.py index 9846644f5..7e568ebde 100644 --- a/backend/app/api/routes/ip.py +++ b/backend/app/api/routes/ip.py @@ -1,6 +1,7 @@ -from fastapi import APIRouter, Request, Response, HTTPException import ipaddress -from typing import Dict, Any +from typing import Any + +from fastapi import APIRouter, HTTPException, Request, Response from app.api.deps import CurrentUser @@ -12,12 +13,12 @@ router = APIRouter(prefix="/ip", tags=["ip"]) async def get_client_ip(request: Request): """ 获取客户端IP地址 - + 返回客户端的公网IP地址,通常是通过代理转发的X-Forwarded-For头获取 """ # 尝试从各种HTTP头获取IP地址 client_ip = _extract_client_ip(request) - + return {"ip": client_ip} @@ -25,12 +26,12 @@ async def get_client_ip(request: Request): async def get_client_ip_text(request: Request): """ 获取客户端IP地址(纯文本格式) - + 返回客户端的公网IP地址,以纯文本格式 """ # 尝试从各种HTTP头获取IP地址 client_ip = _extract_client_ip(request) - + return Response(content=client_ip, media_type="text/plain") @@ -38,7 +39,7 @@ async def get_client_ip_text(request: Request): async def get_request_headers(request: Request): """ 获取请求的所有头信息 - + 返回包含所有HTTP请求头的字典 """ return {"headers": dict(request.headers)} @@ -49,28 +50,28 @@ async def get_request_headers(request: Request): async def get_secure_ip(request: Request, current_user: CurrentUser): """ 获取客户端IP地址(需要登录) - + 这是一个需要用户登录的示例端点 """ client_ip = _extract_client_ip(request) - + return { "ip": client_ip, "user_id": str(current_user.id), - "user_email": current_user.email + "user_email": current_user.email, } -@router.get("/analyze/{ip_address}", response_model=Dict[str, Any]) +@router.get("/analyze/{ip_address}", response_model=dict[str, Any]) async def analyze_ip(ip_address: str): """ 分析指定的IP地址 - + 返回IP地址的基本信息,如版本、是否私有等 """ try: ip = ipaddress.ip_address(ip_address) - + result = { "ip": str(ip), "version": f"IPv{ip.version}", @@ -80,51 +81,51 @@ async def analyze_ip(ip_address: str): "is_loopback": ip.is_loopback, "is_link_local": ip.is_link_local, } - + # IPv6特有属性 if ip.version == 6: ipv6 = ipaddress.IPv6Address(ip_address) - result.update({ - "is_site_local": ipv6.is_site_local, - "ipv4_mapped": ipv6.ipv4_mapped is not None, - "teredo": ipv6.teredo is not None, - "sixtofour": ipv6.sixtofour is not None - }) - + result.update( + { + "is_site_local": ipv6.is_site_local, + "ipv4_mapped": ipv6.ipv4_mapped is not None, + "teredo": ipv6.teredo is not None, + "sixtofour": ipv6.sixtofour is not None, + } + ) + # 尝试获取反向DNS查询名称 try: import socket + result["reverse_pointer"] = socket.gethostbyaddr(ip_address)[0] except (socket.herror, socket.gaierror): result["reverse_pointer"] = None - + return result except ValueError: - raise HTTPException( - status_code=400, - detail=f"无效的IP地址: {ip_address}" - ) + raise HTTPException(status_code=400, detail=f"无效的IP地址: {ip_address}") def _extract_client_ip(request: Request) -> str: """ 从请求中提取客户端IP地址 - + 按照优先级尝试从不同的HTTP头获取 """ # 检查常见的代理头 for header in [ - "X-Forwarded-For", - "X-Real-IP", + "X-Forwarded-For", + "X-Real-IP", "CF-Connecting-IP", # Cloudflare - "True-Client-IP", # Akamai/Cloudflare - "X-Client-IP" + "True-Client-IP", # Akamai/Cloudflare + "X-Client-IP", ]: if header_value := request.headers.get(header): # 如果是X-Forwarded-For,可能包含多个IP,取第一个 if header == "X-Forwarded-For" and "," in header_value: return header_value.split(",")[0].strip() return header_value.strip() - + # 如果没有代理头,使用直接连接的客户端地址 - return request.client.host if request.client else "unknown" \ No newline at end of file + return request.client.host if request.client else "unknown" diff --git a/backend/app/email-templates/build/new_account.html b/backend/app/email-templates/build/new_account.html index 344505033..0b83ceb67 100644 --- a/backend/app/email-templates/build/new_account.html +++ b/backend/app/email-templates/build/new_account.html @@ -22,4 +22,4 @@
{{ project_name }} - New Account
Welcome to your new account!
Here are your account details:
Username: {{ username }}
Password: {{ password }}
Go to Dashboard

\ No newline at end of file + diff --git a/backend/app/email-templates/build/reset_password.html b/backend/app/email-templates/build/reset_password.html index 4148a5b77..55297d094 100644 --- a/backend/app/email-templates/build/reset_password.html +++ b/backend/app/email-templates/build/reset_password.html @@ -22,4 +22,4 @@
{{ project_name }} - Password Recovery
Hello {{ username }}
We've received a request to reset your password. You can do it by clicking the button below:
Reset password
Or copy and paste the following link into your browser:
This password will expire in {{ valid_hours }} hours.

If you didn't request a password recovery you can disregard this email.
\ No newline at end of file +
If you didn't request a password recovery you can disregard this email.
diff --git a/backend/app/email-templates/build/test_email.html b/backend/app/email-templates/build/test_email.html index 04d0d8509..c9609c910 100644 --- a/backend/app/email-templates/build/test_email.html +++ b/backend/app/email-templates/build/test_email.html @@ -22,4 +22,4 @@
{{ project_name }}
Test email for: {{ email }}

\ No newline at end of file + diff --git a/backend/app/tests/scripts/test_backend_pre_start.py b/backend/app/tests/scripts/test_backend_pre_start.py index 631690fcf..0591da0c8 100644 --- a/backend/app/tests/scripts/test_backend_pre_start.py +++ b/backend/app/tests/scripts/test_backend_pre_start.py @@ -24,10 +24,10 @@ def test_init_successful_connection() -> None: except Exception: connection_successful = False - assert ( - connection_successful - ), "The database connection should be successful and not raise an exception." + assert connection_successful, ( + "The database connection should be successful and not raise an exception." + ) - assert session_mock.exec.called_once_with( - select(1) - ), "The session should execute a select statement once." + assert session_mock.exec.called_once_with(select(1)), ( + "The session should execute a select statement once." + ) diff --git a/backend/app/tests/scripts/test_test_pre_start.py b/backend/app/tests/scripts/test_test_pre_start.py index a176f380d..a2ead3cfd 100644 --- a/backend/app/tests/scripts/test_test_pre_start.py +++ b/backend/app/tests/scripts/test_test_pre_start.py @@ -24,10 +24,10 @@ def test_init_successful_connection() -> None: except Exception: connection_successful = False - assert ( - connection_successful - ), "The database connection should be successful and not raise an exception." + assert connection_successful, ( + "The database connection should be successful and not raise an exception." + ) - assert session_mock.exec.called_once_with( - select(1) - ), "The session should execute a select statement once." + assert session_mock.exec.called_once_with(select(1)), ( + "The session should execute a select statement once." + ) diff --git a/development.md b/development.md index d7d41d73f..0a7c6f226 100644 --- a/development.md +++ b/development.md @@ -204,4 +204,4 @@ Adminer: http://localhost.tiangolo.com:8080 Traefik UI: http://localhost.tiangolo.com:8090 -MailCatcher: http://localhost.tiangolo.com:1080 \ No newline at end of file +MailCatcher: http://localhost.tiangolo.com:1080 diff --git a/frontend/.gitignore b/frontend/.gitignore index 75e25e0ef..093ec6dcb 100644 --- a/frontend/.gitignore +++ b/frontend/.gitignore @@ -27,4 +27,4 @@ openapi.json /playwright-report/ /blob-report/ /playwright/.cache/ -/playwright/.auth/ \ No newline at end of file +/playwright/.auth/ diff --git a/frontend/src/components/Pending/PendingItems.tsx b/frontend/src/components/Pending/PendingItems.tsx index 4ffa13803..3a82634f0 100644 --- a/frontend/src/components/Pending/PendingItems.tsx +++ b/frontend/src/components/Pending/PendingItems.tsx @@ -4,7 +4,7 @@ import { SkeletonText } from "../ui/skeleton" const PendingItems = () => { const { t } = useTranslation() - + return ( diff --git a/frontend/src/components/UserSettings/DeleteAccount.tsx b/frontend/src/components/UserSettings/DeleteAccount.tsx index 5be4e6c2a..936e49a76 100644 --- a/frontend/src/components/UserSettings/DeleteAccount.tsx +++ b/frontend/src/components/UserSettings/DeleteAccount.tsx @@ -5,7 +5,7 @@ import DeleteConfirmation from "./DeleteConfirmation" const DeleteAccount = () => { const { t } = useTranslation() - + return ( diff --git a/frontend/src/components/ui/password-input.tsx b/frontend/src/components/ui/password-input.tsx index c73550016..2c9573ffa 100644 --- a/frontend/src/components/ui/password-input.tsx +++ b/frontend/src/components/ui/password-input.tsx @@ -97,7 +97,7 @@ export const PasswordInput = forwardRef( const VisibilityTrigger = forwardRef( function VisibilityTrigger(props, ref) { const { t } = useTranslation() - + return ( { const { t } = useTranslation() - + const showSuccessToast = (description: string) => { toaster.create({ title: t("common.success"), diff --git a/frontend/src/i18n/index.ts b/frontend/src/i18n/index.ts index 9b0d944f9..c79d77e8f 100644 --- a/frontend/src/i18n/index.ts +++ b/frontend/src/i18n/index.ts @@ -21,15 +21,15 @@ i18n resources, fallbackLng: 'en', debug: false, - + interpolation: { escapeValue: false }, - + detection: { order: ['localStorage', 'navigator', 'htmlTag'], caches: ['localStorage'] } }) -export default i18n \ No newline at end of file +export default i18n diff --git a/frontend/src/routes/_layout/admin.tsx b/frontend/src/routes/_layout/admin.tsx index e0992eb8d..1ef53e574 100644 --- a/frontend/src/routes/_layout/admin.tsx +++ b/frontend/src/routes/_layout/admin.tsx @@ -117,7 +117,7 @@ function UsersTable() { function Admin() { const { t } = useTranslation() - + return ( diff --git a/frontend/src/routes/_layout/items.tsx b/frontend/src/routes/_layout/items.tsx index e2a4f3f26..b760879b3 100644 --- a/frontend/src/routes/_layout/items.tsx +++ b/frontend/src/routes/_layout/items.tsx @@ -135,7 +135,7 @@ function ItemsTable() { function Items() { const { t } = useTranslation() - + return ( diff --git a/frontend/src/routes/_layout/settings.tsx b/frontend/src/routes/_layout/settings.tsx index 19a2745d6..ca62b88bc 100644 --- a/frontend/src/routes/_layout/settings.tsx +++ b/frontend/src/routes/_layout/settings.tsx @@ -11,14 +11,14 @@ import useAuth from "@/hooks/useAuth" function UserSettings() { const { user: currentUser } = useAuth() const { t } = useTranslation() - + const tabsConfig = [ { value: "my-profile", title: t("user.profile"), component: UserInformation }, { value: "password", title: t("user.changePassword"), component: ChangePassword }, { value: "appearance", title: t("user.appearance"), component: Appearance }, { value: "danger-zone", title: t("user.deleteAccount"), component: DeleteAccount }, ] - + const finalTabs = currentUser?.is_superuser ? tabsConfig.slice(0, 3) : tabsConfig diff --git a/hooks/post_gen_project.py b/hooks/post_gen_project.py index 2ca5260da..df951026f 100644 --- a/hooks/post_gen_project.py +++ b/hooks/post_gen_project.py @@ -1,6 +1,5 @@ from pathlib import Path - path: Path for path in Path(".").glob("**/*.sh"): data = path.read_bytes()