Implement session management with login functionality and add admin credentials to configuration
This commit is contained in:
parent
ac7be6ee02
commit
657188dc27
6
app/.env
6
app/.env
@ -19,4 +19,8 @@ LOG_LEVEL=INFO
|
|||||||
# 端口数==条数 从172.30.168.2开始
|
# 端口数==条数 从172.30.168.2开始
|
||||||
PORT_NUM=10
|
PORT_NUM=10
|
||||||
|
|
||||||
MAX_ONLINE=10000
|
MAX_ONLINE=10000
|
||||||
|
|
||||||
|
ADMIN_USERNAME=heping
|
||||||
|
ADMIN_PASSWORD=He_Ping551
|
||||||
|
SESSION_SECRET=sinehiunsdkfi
|
||||||
@ -17,6 +17,10 @@ class Settings(BaseModel):
|
|||||||
log_level: str = os.getenv("LOG_LEVEL", "INFO")
|
log_level: str = os.getenv("LOG_LEVEL", "INFO")
|
||||||
port_num: int = int(os.getenv("PORT_NUM", 3))
|
port_num: int = int(os.getenv("PORT_NUM", 3))
|
||||||
max_online: int = int(os.getenv("MAX_ONLINE", 3))
|
max_online: int = int(os.getenv("MAX_ONLINE", 3))
|
||||||
|
# 前台登录配置
|
||||||
|
admin_username: str = os.getenv("ADMIN_USERNAME", "admin")
|
||||||
|
admin_password: str = os.getenv("ADMIN_PASSWORD", "admin")
|
||||||
|
session_secret: str = os.getenv("SESSION_SECRET", "change-me-please")
|
||||||
|
|
||||||
|
|
||||||
settings = Settings()
|
settings = Settings()
|
||||||
|
|||||||
30
app/main.py
30
app/main.py
@ -1,15 +1,19 @@
|
|||||||
from fastapi import FastAPI, Request
|
from fastapi import FastAPI, Request, Form
|
||||||
from fastapi.templating import Jinja2Templates
|
from fastapi.templating import Jinja2Templates
|
||||||
from fastapi.staticfiles import StaticFiles
|
from fastapi.staticfiles import StaticFiles
|
||||||
from fastapi.responses import HTMLResponse
|
from fastapi.responses import HTMLResponse, RedirectResponse
|
||||||
from .config import settings
|
from .config import settings
|
||||||
from .routers.proxy import router as proxy_router
|
from .routers.proxy import router as proxy_router
|
||||||
|
from starlette.middleware.sessions import SessionMiddleware
|
||||||
|
|
||||||
app = FastAPI(title="EIP Rotation Service")
|
app = FastAPI(title="EIP Rotation Service")
|
||||||
|
|
||||||
# 设置模板目录
|
# 设置模板目录
|
||||||
templates = Jinja2Templates(directory="app/templates")
|
templates = Jinja2Templates(directory="app/templates")
|
||||||
|
|
||||||
|
# 会话中间件(用于简单登录态)
|
||||||
|
app.add_middleware(SessionMiddleware, secret_key=settings.session_secret)
|
||||||
|
|
||||||
|
|
||||||
@app.get("/health")
|
@app.get("/health")
|
||||||
def health_check():
|
def health_check():
|
||||||
@ -19,6 +23,8 @@ def health_check():
|
|||||||
@app.get("/", response_class=HTMLResponse)
|
@app.get("/", response_class=HTMLResponse)
|
||||||
async def index(request: Request, id: str = None):
|
async def index(request: Request, id: str = None):
|
||||||
"""主页 - IP轮换控制面板"""
|
"""主页 - IP轮换控制面板"""
|
||||||
|
if not request.session.get("authenticated"):
|
||||||
|
return RedirectResponse(url="/login", status_code=302)
|
||||||
return templates.TemplateResponse("index.html", {
|
return templates.TemplateResponse("index.html", {
|
||||||
"request": request,
|
"request": request,
|
||||||
"client_id": id or "1" # 默认ID为1
|
"client_id": id or "1" # 默认ID为1
|
||||||
@ -28,3 +34,23 @@ async def index(request: Request, id: str = None):
|
|||||||
app.include_router(proxy_router, prefix="/proxy", tags=["proxy"])
|
app.include_router(proxy_router, prefix="/proxy", tags=["proxy"])
|
||||||
|
|
||||||
|
|
||||||
|
@app.get("/login", response_class=HTMLResponse)
|
||||||
|
async def login_page(request: Request):
|
||||||
|
return templates.TemplateResponse("login.html", {"request": request, "error": None})
|
||||||
|
|
||||||
|
|
||||||
|
@app.post("/login")
|
||||||
|
async def login_submit(request: Request, username: str = Form(...), password: str = Form(...)):
|
||||||
|
if username == settings.admin_username and password == settings.admin_password:
|
||||||
|
request.session["authenticated"] = True
|
||||||
|
return RedirectResponse(url="/", status_code=302)
|
||||||
|
# 登录失败,回到登录页并提示
|
||||||
|
return templates.TemplateResponse("login.html", {"request": request, "error": "用户名或密码错误"}, status_code=401)
|
||||||
|
|
||||||
|
|
||||||
|
@app.get("/logout")
|
||||||
|
async def logout(request: Request):
|
||||||
|
request.session.clear()
|
||||||
|
return RedirectResponse(url="/login", status_code=302)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
47
app/templates/login.html
Normal file
47
app/templates/login.html
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="zh-CN">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>登录 - EIP 控制面板</title>
|
||||||
|
<style>
|
||||||
|
body { font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); min-height: 100vh; display: flex; align-items: center; justify-content: center; margin: 0; }
|
||||||
|
.card { width: 360px; background: #fff; padding: 32px; border-radius: 16px; box-shadow: 0 20px 40px rgba(0,0,0,0.12); }
|
||||||
|
h1 { margin: 0 0 8px; font-weight: 400; text-align: center; }
|
||||||
|
p { margin: 0 0 24px; color: #666; text-align: center; }
|
||||||
|
label { display: block; font-weight: 600; margin: 12px 0 8px; color: #333; }
|
||||||
|
input[type="text"], input[type="password"] { width: 100%; padding: 10px 12px; border: 1px solid #ddd; border-radius: 8px; font-size: 14px; }
|
||||||
|
.btn { width: 100%; margin-top: 16px; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: #fff; border: none; padding: 10px 14px; border-radius: 10px; cursor: pointer; font-size: 15px; }
|
||||||
|
.error { margin-top: 12px; background: #f8d7da; color: #721c24; border: 1px solid #f5c6cb; padding: 10px; border-radius: 8px; display: {{ 'block' if error else 'none' }}; }
|
||||||
|
</style>
|
||||||
|
<script>
|
||||||
|
document.addEventListener('DOMContentLoaded', function() {
|
||||||
|
const u = document.getElementById('username');
|
||||||
|
if (u) u.focus();
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
<script>
|
||||||
|
(function(){
|
||||||
|
if (window.top !== window.self) {
|
||||||
|
window.top.location = window.location;
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
</script>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="card">
|
||||||
|
<h1>登录</h1>
|
||||||
|
<p>进入 EIP IP 轮换控制面板</p>
|
||||||
|
<form method="post" action="/login">
|
||||||
|
<label for="username">用户名</label>
|
||||||
|
<input id="username" name="username" type="text" autocomplete="username" required>
|
||||||
|
<label for="password">密码</label>
|
||||||
|
<input id="password" name="password" type="password" autocomplete="current-password" required>
|
||||||
|
<button class="btn" type="submit">登录</button>
|
||||||
|
</form>
|
||||||
|
<div class="error">{{ error }}</div>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
|
||||||
|
|
||||||
Loading…
x
Reference in New Issue
Block a user