You've already forked DataMate
feature: LabelStudio jumps without login (#201)
This commit is contained in:
@@ -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");
|
||||||
|
|||||||
@@ -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");
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
Reference in New Issue
Block a user