사용자별 격리 시스템 + 사용자 관리 + 라이브 PnL%
# 사용자별 격리 - JWT 토큰에 uid 추가 (auth.get_uid 헬퍼) - PostgreSQL — exchange_credentials/automation_config/trades/signal_events 에 user_id BIGINT - SQLite user_settings 테이블 신설 (글로벌 settings 는 옛 호환) - 모든 DB 함수 시그니처에 user_id 인자 추가 — 다른 사용자 데이터 절대 접근 불가 - alert_state — 모든 dict key 가 (user_id, ...) tuple 로 계층화 - core_logic alert_loop — 활성 사용자 순회 + 각자 settings/symbol/텔레그램 적용 - ensure_user_defaults() / ensure_user_automation() — 첫 사용 시 자동 시드 # 사용자 관리 (admin only) - users_db: delete_user / admin_reset_password / set_role - /api/users POST DELETE PUT password PUT role (본인 강등 / 마지막 admin 보호) - /admin/users 페이지 — 등록/삭제/role 토글/비번 reset 모달 - 사이드바 adminOnly 필터 — admin role 만 메뉴 노출 # 대시보드 개선 - 모바일 / 범례 토글 (모바일 60 캔들, 데스크톱 200) - 트레이드 이력: open 트레이드 실시간 PnL% (Binance ticker 호출 + 방향별 계산) - 메트릭 카드 분리 (실거래 vs 실시간 open) # 안정성 - api.ts: error.detail array/object 안전 처리 ([object Object] 방지) - Chart.tsx: Plotly yaxis title 객체 형태 + 모바일 height 동적 조정 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
+52
@@ -168,6 +168,58 @@ def create_user(username: str, password: str, role: str = "user") -> Optional[in
|
||||
return None
|
||||
|
||||
|
||||
def delete_user(user_id: int) -> bool:
|
||||
if not _enabled():
|
||||
return False
|
||||
with _lock:
|
||||
conn = _get_conn()
|
||||
if conn is None:
|
||||
return False
|
||||
try:
|
||||
with conn.cursor() as cur:
|
||||
cur.execute("DELETE FROM users WHERE id=%s", (user_id,))
|
||||
return cur.rowcount > 0
|
||||
except Exception as e:
|
||||
print(f"[users_db] delete_user 실패: {e}")
|
||||
return False
|
||||
|
||||
|
||||
def admin_reset_password(user_id: int, new_password: str) -> bool:
|
||||
"""관리자가 다른 사용자 비번 초기화. 본인 비번 변경은 change_password() 사용."""
|
||||
if not _enabled() or len(new_password) < 6:
|
||||
return False
|
||||
with _lock:
|
||||
conn = _get_conn()
|
||||
if conn is None:
|
||||
return False
|
||||
try:
|
||||
with conn.cursor() as cur:
|
||||
cur.execute(
|
||||
"UPDATE users SET password_hash=%s WHERE id=%s",
|
||||
(_hash(new_password), user_id),
|
||||
)
|
||||
return cur.rowcount > 0
|
||||
except Exception as e:
|
||||
print(f"[users_db] admin_reset_password 실패: {e}")
|
||||
return False
|
||||
|
||||
|
||||
def set_role(user_id: int, role: str) -> bool:
|
||||
if not _enabled() or role not in ("admin", "user"):
|
||||
return False
|
||||
with _lock:
|
||||
conn = _get_conn()
|
||||
if conn is None:
|
||||
return False
|
||||
try:
|
||||
with conn.cursor() as cur:
|
||||
cur.execute("UPDATE users SET role=%s WHERE id=%s", (role, user_id))
|
||||
return cur.rowcount > 0
|
||||
except Exception as e:
|
||||
print(f"[users_db] set_role 실패: {e}")
|
||||
return False
|
||||
|
||||
|
||||
def list_users():
|
||||
if not _enabled():
|
||||
return []
|
||||
|
||||
Reference in New Issue
Block a user