개발 가이드
Not translated
This page is currently being translated. We will complete the translation as soon as possible.
This page is currently being translated. We will complete the translation as soon as possible.
BioStar X와 연동되는 플러그인의 실제 개발 방법과 구현 예시를 안내합니다.
필수 엔드포인트 구현
BioStar X와 연동되는 플러그인을 개발하려면 다음 두 개의 엔드포인트를 반드시 구현해야 합니다. 이 문서에서는 /bsx
를 예시 경로로 사용합니다.
플러그인 등록 엔드포인트
GET /bsx
플러그인을 등록할 때 BioStar X Service Manager에서 서버 접근성을 확인하기 위해 호출하는 엔드포인트입니다.
요청 형식
GET /bsx
Headers:
X-BioStar-Ping: <ping_token>
구현 요구 사항
Python FastAPI
from fastapi import FastAPI, Request
from fastapi.responses import JSONResponse
app = FastAPI()
@app.get("/bsx")
async def bsx_ping(request: Request):
"""BioStar X 플러그인 등록용 ping 엔드포인트"""
ping_token = request.headers.get("x-biostar-ping")
# 응답에 ping 토큰 반환
response_data = {"token": ping_token}
response = JSONResponse(content=response_data)
return response
응답 형식
Success (200 OK)
{
"token": "<ping_token_value>"
}
테스트
curl
명령어를 사용해 수동으로 엔드포인트를 테스트할 수 있습니다.
curl -H "X-BioStar-Ping: test123" http://localhost:8000/bsx
사용자 인증 콜백 엔드포인트
POST /bsx
사용자가 BioStar X 웹 인터페이스에서 플러그인 아이콘을 클릭할 때 호출되는 엔드포인트입니다. 이 과정에서 BioStar X에 로그한 사용자의 세션(session)을 요청해 응답을 받습니다.
Info
BioStar X와 플러그인 간의 보안 통신을 위해 인증서가 필요합니다. 인증서는 플러그인 등록 후 해당 플러그인 상세 페이지에서 다운로드할 수 있습니다. 자세한 내용은 다음 문서를 참고하세요.
요청 형식
POST /bsx
Content-Type: application/x-www-form-urlencoded
user_id={user_id}&plugin_id={plugin_id}
구현 요구사항
-
매개변수 검증:
user_id
와plugin_id
필수 값 확인 -
AES 키 생성: 보안 통신용 32바이트 키 생성
-
키 암호화: RSA 공개키로 AES 키 암호화(예시 코드에서는
public_key.pem
파일 이름으로 가정) -
세션 브리지 요청: BioStar X 서버에 세션 요청
-
세션 복호화: 받은 암호화된 세션 ID를 AES로 복호화
import uuid
import requests
from fastapi import HTTPException, Form
@app.post("/bsx")
async def bsx_callback(
request: Request,
user_id: str = Form(None, description="사용자 ID"),
plugin_id: str = Form(None, description="플러그인 ID")
):
"""BioStar X 플러그인 콜백 처리"""
# 1. 필수 매개변수 검증
if not user_id:
raise HTTPException(status_code=400, detail="user_id가 필요합니다")
if not plugin_id:
raise HTTPException(status_code=400, detail="plugin_id가 필요합니다")
# 2. 보안 통신을 위한 AES 키 생성
key = uuid.uuid4().hex # 32바이트 키
# 3. RSA 공개키로 키 암호화
encrypted_key = encrypt_with_public_key(key, "public_key.pem")
# 4. BioStar 서버에 세션 브리지 요청
payload = {
"user_id": user_id,
"plugin_id": plugin_id,
"key": encrypted_key
}
response = requests.post(
f"https://{BIOSTAR_SERVER}/api/session/bridge",
json=payload,
verify=False
)
# 5. 응답 처리
if response.status_code == 200:
data = response.json()
if data.get("Response", {}).get("code") == "0":
# 세션 ID 복호화
encrypted_session = data.get("bs_session_id")
bs_session_id = decrypt_aes256_base64(encrypted_session, key)
else:
# BioStar X 오류 처리
error_msg = data.get("Response", {}).get("message", "알 수 없는 오류")
raise HTTPException(status_code=400, detail=f"BioStar X 오류: {error_msg}")
else:
raise HTTPException(status_code=500, detail="BioStar X 서버 연결 실패")
테스트
curl
명령어를 사용해 수동으로 엔드포인트를 테스트할 수 있습니다.
curl -X POST http://localhost:8000/bsx \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "user_id={user_id}&plugin_id={your_plugin_id}"
암호화 구현
다음 암호화 함수를 구현해야 합니다.
RSA 공개키 암호화
from cryptography.hazmat.primitives import serialization, padding
import base64
def encrypt_with_public_key(message: str, public_key_path: str) -> str:
"""RSA 공개키로 메시지 암호화"""
with open(public_key_path, 'rb') as key_file:
public_key = serialization.load_pem_public_key(key_file.read())
message_bytes = message.encode('utf-8')
encrypted = public_key.encrypt(message_bytes, padding.PKCS1v15())
return base64.b64encode(encrypted).decode('utf-8')
AES-256 복호화
from Crypto.Cipher import AES
from Crypto.Util.Padding import unpad
import base64
def decrypt_aes256_base64(encrypted_data: str, key: str) -> str:
"""AES256으로 암호화된 base64 데이터 복호화"""
key_bytes = key.encode('utf-8')
# 32바이트 키 보장
if len(key_bytes) != 32:
if len(key_bytes) < 32:
key_bytes = key_bytes.ljust(32, b'\0')
else:
key_bytes = key_bytes[:32]
cipher_text = base64.b64decode(encrypted_data)
iv = key_bytes[:16] # 첫 16바이트를 IV로 사용
cipher = AES.new(key_bytes, AES.MODE_CBC, iv)
decrypted = unpad(cipher.decrypt(cipher_text), AES.block_size)
return decrypted.decode('utf-8')