feat(backend): Django + OpenCode 백엔드 추가 — 5_django_backend/
앱이 기대하는 base-backend 계약(envelope·JWT·/chat/stream SSE)을 그대로 구현. Django 는 로그인·세션 미러(SQLite/PG)·OpenCode 이벤트 번역만 맡고, 답변은 전용 OpenCode 인스턴스(opencode/ workspace, codeassist 에이전트)가 만듦. - accounts: 이메일 로그인, access 60분 / refresh 14일, entra/config 는 501 - chat: 세션 목록·검색·메시지·취소 + POST /chat/stream 어댑터(part.delta→token, idle→usage/done, 클라 끊겨도 턴 감시 태스크가 DB 마무리, 첫 이벤트 60초 타임아웃) - OpenCode 1.18 멀티 프로젝트라 모든 요청에 ?directory= 부착 - docs-lib 에 OpenCode SDK 1.18.6 타입 원본, pytest 32 Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,43 @@
|
||||
"""JWT 인증 — Authorization: Bearer 헤더 또는 ?token= 쿼리(EventSource·다운로드용).
|
||||
토큰 발급은 issue_tokens() — 프론트 TokenResponse 모양."""
|
||||
|
||||
from rest_framework_simplejwt.authentication import JWTAuthentication
|
||||
from rest_framework_simplejwt.exceptions import InvalidToken, TokenError
|
||||
from rest_framework_simplejwt.tokens import AccessToken, RefreshToken
|
||||
|
||||
|
||||
class BearerOrQueryJWTAuthentication(JWTAuthentication):
|
||||
def authenticate(self, request):
|
||||
result = super().authenticate(request)
|
||||
if result is not None:
|
||||
return result
|
||||
raw = request.query_params.get("token") if hasattr(request, "query_params") else None
|
||||
raw = raw or request.GET.get("token")
|
||||
if not raw:
|
||||
return None
|
||||
validated = self.get_validated_token(raw)
|
||||
return self.get_user(validated), validated
|
||||
|
||||
|
||||
def issue_tokens(user, refresh: RefreshToken | None = None) -> dict:
|
||||
"""TokenResponse. refresh 를 주면 그걸 유지하고 access 만 새로 찍음(회전 안 함)."""
|
||||
refresh = refresh or RefreshToken.for_user(user)
|
||||
access = refresh.access_token
|
||||
return {
|
||||
"token": str(access),
|
||||
"tokenExpirationTime": int(access["exp"]),
|
||||
"refreshToken": str(refresh),
|
||||
"refreshTokenExpirationTime": int(refresh["exp"]),
|
||||
"tokenType": "bearer",
|
||||
"user": user.as_payload(),
|
||||
}
|
||||
|
||||
|
||||
def user_from_token(raw: str):
|
||||
"""순수 Django(async) 뷰용 — access 토큰 → 사용자, 실패 시 None."""
|
||||
if not raw:
|
||||
return None
|
||||
try:
|
||||
return BearerOrQueryJWTAuthentication().get_user(AccessToken(raw))
|
||||
except (InvalidToken, TokenError, Exception):
|
||||
return None
|
||||
Reference in New Issue
Block a user