Files
leehc991028andClaude Fable 5.1 2869985139 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>
2026-09-16 21:28:31 +09:00

44 lines
1.7 KiB
Python

"""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