11import { useState } from 'react' ;
22import { useNavigate , useLocation } from 'react-router-dom' ;
33import { setApiKey , getApiKey , api } from '../api/client' ;
4- import { KeyRound , QrCode , Smartphone , ShieldCheck } from 'lucide-react' ;
4+ import { KeyRound , QrCode , Smartphone , ShieldCheck , FileKey } from 'lucide-react' ;
55
66export default function Login ( ) {
77 const location = useLocation ( ) ;
@@ -12,7 +12,7 @@ export default function Login() {
1212 const [ authed , setAuthed ] = useState ( skipKey ) ;
1313 const navigate = useNavigate ( ) ;
1414
15- const [ loginMethod , setLoginMethod ] = useState < 'qr' | 'phone' > ( 'qr' ) ;
15+ const [ loginMethod , setLoginMethod ] = useState < 'qr' | 'phone' | 'session' > ( 'qr' ) ;
1616
1717 const [ qrImage , setQrImage ] = useState ( '' ) ;
1818 const [ qrStatus , setQrStatus ] = useState ( '' ) ;
@@ -23,6 +23,9 @@ export default function Login() {
2323
2424 const [ password , setPassword ] = useState ( '' ) ;
2525
26+ const [ sessionString , setSessionString ] = useState ( '' ) ;
27+ const [ sessionLoading , setSessionLoading ] = useState ( false ) ;
28+
2629 async function handleKeySubmit ( e : React . FormEvent ) {
2730 e . preventDefault ( ) ;
2831 setApiKey ( key ) ;
@@ -124,6 +127,24 @@ export default function Login() {
124127 }
125128 }
126129
130+ async function handleSessionImport ( e : React . FormEvent ) {
131+ e . preventDefault ( ) ;
132+ setError ( '' ) ;
133+ setSessionLoading ( true ) ;
134+ try {
135+ const data = await api < { status : string ; error ?: string } > (
136+ '/api/auth/session-login' ,
137+ { method : 'POST' , body : JSON . stringify ( { session_string : sessionString } ) } ,
138+ ) ;
139+ if ( data . status === 'success' ) navigate ( '/' ) ;
140+ else if ( data . error ) setError ( data . error ) ;
141+ } catch ( e : unknown ) {
142+ setError ( String ( e ) ) ;
143+ } finally {
144+ setSessionLoading ( false ) ;
145+ }
146+ }
147+
127148 const show2fa = qrStatus === '2fa_required' || phoneStatus === '2fa_required' ;
128149
129150 const inputClass = 'w-full px-3 py-2 bg-slate-800 border border-slate-600 rounded-lg text-slate-200 placeholder-slate-500 focus:outline-none focus:ring-2 focus:ring-cyan-500/50 focus:border-cyan-500 transition-colors' ;
@@ -181,6 +202,17 @@ export default function Login() {
181202 < Smartphone className = "w-4 h-4" />
182203 手机号登录
183204 </ button >
205+ < button
206+ onClick = { ( ) => { setLoginMethod ( 'session' ) ; setError ( '' ) ; } }
207+ className = { `flex-1 py-2 text-sm font-medium border-b-2 transition-colors flex items-center justify-center gap-1.5 ${
208+ loginMethod === 'session'
209+ ? 'border-cyan-400 text-cyan-400'
210+ : 'border-transparent text-slate-500 hover:text-slate-300'
211+ } `}
212+ >
213+ < FileKey className = "w-4 h-4" />
214+ Session 导入
215+ </ button >
184216 </ div >
185217
186218 { loginMethod === 'qr' && (
@@ -273,6 +305,32 @@ export default function Login() {
273305 </ >
274306 ) }
275307
308+ { loginMethod === 'session' && (
309+ < form onSubmit = { handleSessionImport } className = "space-y-3" >
310+ < div >
311+ < label className = "block text-sm font-medium text-slate-400 mb-1" >
312+ StringSession 字符串
313+ </ label >
314+ < textarea
315+ value = { sessionString }
316+ onChange = { ( e ) => setSessionString ( e . target . value ) }
317+ className = { inputClass + ' resize-none h-24 font-mono text-xs' }
318+ placeholder = "粘贴 Telethon StringSession 字符串..."
319+ />
320+ < p className = "text-xs text-slate-500 mt-1" >
321+ 通过 Telethon 生成的 Session 字符串
322+ </ p >
323+ </ div >
324+ < button
325+ type = "submit"
326+ disabled = { ! sessionString . trim ( ) || sessionLoading }
327+ className = { primaryBtn + ( ( ! sessionString . trim ( ) || sessionLoading ) ? ' opacity-60 cursor-not-allowed' : '' ) }
328+ >
329+ { sessionLoading ? '验证中...' : '导入 Session' }
330+ </ button >
331+ </ form >
332+ ) }
333+
276334 < button
277335 onClick = { ( ) => navigate ( '/' ) }
278336 className = "w-full py-2 border border-slate-600 rounded-lg text-slate-400 hover:bg-slate-700 hover:text-slate-200 transition-colors"
0 commit comments