Backend: FastAPI + SQLAlchemy + SQLite Frontend: React + Vite + Ant Design (serif Anthropic theme) Media server: MediaMTX with edge node support Load balancing: round-robin / weighted / least-conn Multicast detection with automatic fallback Transcoding support via FFmpeg (configurable quality) hls.js player with line & quality switching Admin panel with password auth Windows compatible scripts
62 lines
1.8 KiB
TypeScript
62 lines
1.8 KiB
TypeScript
import { useState } from 'react'
|
|
import { useNavigate } from 'react-router-dom'
|
|
import { Card, Form, Input, Button, Typography, message, Space } from 'antd'
|
|
import { LockOutlined } from '@ant-design/icons'
|
|
import { login } from '../api/auth'
|
|
|
|
const { Title, Text } = Typography
|
|
|
|
export default function LoginPage() {
|
|
const [loading, setLoading] = useState(false)
|
|
const navigate = useNavigate()
|
|
|
|
const handleSubmit = async (values: { password: string }) => {
|
|
setLoading(true)
|
|
try {
|
|
await login(values.password)
|
|
message.success('Welcome back')
|
|
navigate('/admin')
|
|
} catch {
|
|
message.error('Invalid password')
|
|
} finally {
|
|
setLoading(false)
|
|
}
|
|
}
|
|
|
|
return (
|
|
<div style={{
|
|
minHeight: '100vh',
|
|
display: 'flex',
|
|
alignItems: 'center',
|
|
justifyContent: 'center',
|
|
background: '#FAF8F5',
|
|
}}>
|
|
<Card style={{ width: 400, boxShadow: '0 2px 16px rgba(0,0,0,0.04)' }}>
|
|
<Space direction="vertical" size="large" style={{ width: '100%' }}>
|
|
<div style={{ textAlign: 'center' }}>
|
|
<Title level={2} style={{ marginBottom: 4 }}>LiveStream</Title>
|
|
<Text type="secondary">Admin Panel</Text>
|
|
</div>
|
|
<Form onFinish={handleSubmit} layout="vertical">
|
|
<Form.Item
|
|
name="password"
|
|
rules={[{ required: true, message: 'Enter your password' }]}
|
|
>
|
|
<Input.Password
|
|
prefix={<LockOutlined />}
|
|
placeholder="Password"
|
|
size="large"
|
|
/>
|
|
</Form.Item>
|
|
<Form.Item>
|
|
<Button type="primary" htmlType="submit" loading={loading} block size="large">
|
|
Sign In
|
|
</Button>
|
|
</Form.Item>
|
|
</Form>
|
|
</Space>
|
|
</Card>
|
|
</div>
|
|
)
|
|
}
|