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

View File

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

View File

@@ -1,4 +1,5 @@
import httpx import httpx
import re
from typing import Optional, Dict, Any, List from typing import Optional, Dict, Any, List
from app.core.config import settings 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"]) 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( async def create_project(
self, self,
title: str, title: str,

View File

@@ -2,7 +2,7 @@ from typing import Optional
import math import math
import uuid 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 sqlalchemy.ext.asyncio import AsyncSession
from app.db.session import get_db from app.db.session import get_db
@@ -29,6 +29,39 @@ router = APIRouter(
) )
logger = get_logger(__name__) 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) @router.post("", response_model=StandardResponse[DatasetMappingCreateResponse], status_code=201)
async def create_mapping( async def create_mapping(
request: DatasetMappingCreateRequest, request: DatasetMappingCreateRequest,