앱이 기대하는 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>
42 lines
1.5 KiB
Python
42 lines
1.5 KiB
Python
"""사용자 — 프론트 `types/api.ts` UserPayload/UserResponse 모양으로 내보냄.
|
|
로그인 키는 email. username 은 email 과 같은 값으로 채움(Django 필수 칸이라)."""
|
|
|
|
from django.contrib.auth.models import AbstractUser
|
|
from django.db import models
|
|
|
|
|
|
class User(AbstractUser):
|
|
email = models.EmailField("이메일", unique=True)
|
|
user_name = models.CharField("표시 이름", max_length=40, blank=True)
|
|
employee_id = models.CharField("사번", max_length=40, blank=True)
|
|
department = models.CharField("부서", max_length=80, blank=True)
|
|
|
|
class Meta:
|
|
verbose_name = "사용자"
|
|
verbose_name_plural = "사용자"
|
|
|
|
@property
|
|
def role(self) -> str:
|
|
return "ADMIN" if self.is_superuser else "USER"
|
|
|
|
def as_payload(self) -> dict:
|
|
"""UserPayload — 로그인 응답 안의 user."""
|
|
return {
|
|
"id": str(self.id),
|
|
"email": self.email,
|
|
"userName": self.user_name or None,
|
|
"role": self.role,
|
|
"employeeId": self.employee_id or None,
|
|
"department": self.department or None,
|
|
"authProvider": "local",
|
|
}
|
|
|
|
def as_response(self) -> dict:
|
|
"""UserResponse — GET /users/me."""
|
|
return {
|
|
**self.as_payload(),
|
|
"isActive": self.is_active,
|
|
"createdAt": self.date_joined.isoformat(),
|
|
"updatedAt": (self.last_login or self.date_joined).isoformat(),
|
|
}
|