feature: LabelStudio jumps without login (#201)

This commit is contained in:
hefanli
2025-12-25 16:49:06 +08:00
committed by GitHub
parent 87e73d3bf7
commit 29e4a333a9
4 changed files with 230 additions and 157 deletions

View File

@@ -11,7 +11,7 @@ import CardView from "@/components/CardView";
import type { AnnotationTask } from "../annotation.model";
import useFetchData from "@/hooks/useFetchData";
import {
deleteAnnotationTaskByIdUsingDelete,
deleteAnnotationTaskByIdUsingDelete, loginAnnotationUsingGet,
queryAnnotationTasksUsingGet,
syncAnnotationTaskUsingPost,
} from "../annotation.api";
@@ -76,6 +76,7 @@ export default function DataAnnotation() {
if (labelingProjId) {
// only open external Label Studio when we have a configured base url
await loginAnnotationUsingGet(labelingProjId)
if (base) {
const target = `${base}/projects/${labelingProjId}/data`;
window.open(target, "_blank");

View File

@@ -18,6 +18,10 @@ export function deleteAnnotationTaskByIdUsingDelete(mappingId: string) {
return del(`/api/annotation/project/${mappingId}`);
}
export function loginAnnotationUsingGet(mappingId: string) {
return get("/api/annotation/project/${mappingId}/login");
}
// 标签配置管理
export function getTagConfigUsingGet() {
return get("/api/annotation/tags/config");

View File

@@ -1,4 +1,5 @@
import httpx
import re
from typing import Optional, Dict, Any, List
from app.core.config import settings
@@ -94,6 +95,40 @@ class Client:
"""根据数据类型获取标注配置"""
return self.DEFAULT_LABEL_CONFIGS.get(data_type.lower(), self.DEFAULT_LABEL_CONFIGS["image"])
@staticmethod
def get_csrf_token(html: str) -> str:
m = re.search(r'name="csrfmiddlewaretoken"\s+value="([^"]+)"', html)
if not m:
raise IOError("CSRF Token not found")
return m.group(1)
async def login_label_studio(self):
try:
response = await self.client.get("/user/login/")
response.raise_for_status()
body = response.text
set_cookie_headers = response.headers.get_list("set-cookie")
cookie_header = "; ".join(set_cookie_headers)
form = {
"email": settings.label_studio_username,
"password": settings.label_studio_password,
"csrfmiddlewaretoken": self.get_csrf_token(body),
}
headers = {
"Content-Type": "application/x-www-form-urlencoded",
"Cookie": cookie_header,
}
login_response = await self.client.post("/user/login/", data=form, headers=headers)
logger.info(f"response is: {login_response}, {login_response.text}")
return login_response
except httpx.HTTPStatusError as e:
logger.error(f"Login failed HTTP {e.response.status_code}: {e.response.text}")
return None
except Exception as e:
logger.error(f"Error while login: {e}", e)
return None
async def create_project(
self,
title: str,

View File

@@ -2,7 +2,7 @@ from typing import Optional
import math
import uuid
from fastapi import APIRouter, Depends, HTTPException, Query, Path
from fastapi import APIRouter, Depends, HTTPException, Query, Path, Response
from sqlalchemy.ext.asyncio import AsyncSession
from app.db.session import get_db
@@ -29,6 +29,39 @@ router = APIRouter(
)
logger = get_logger(__name__)
@router.get("/{mapping_id}/login")
async def list_mappings(
db: AsyncSession = Depends(get_db)
):
try:
ls_client = LabelStudioClient(base_url=settings.label_studio_base_url,
token=settings.label_studio_user_token)
target_response = await ls_client.login_label_studio()
headers = dict(target_response.headers)
set_cookies = target_response.headers.get_list("set-cookie")
# 删除合并的 Set-Cookie
if "set-cookie" in headers:
del headers["set-cookie"]
# 创建新响应,添加多个 Set-Cookie
response = Response(
content=target_response.content,
status_code=target_response.status_code,
headers=headers
)
# 分别添加每个 Set-Cookie
for cookie in set_cookies:
response.headers.append("set-cookie", cookie)
return response
except HTTPException:
raise
except Exception as e:
logger.error(f"Error while logining in LabelStudio: {e}", e)
raise HTTPException(status_code=500, detail="Internal server error")
@router.post("", response_model=StandardResponse[DatasetMappingCreateResponse], status_code=201)
async def create_mapping(
request: DatasetMappingCreateRequest,