增加了一些功能
This commit is contained in:
+118
@@ -0,0 +1,118 @@
|
||||
package admin
|
||||
|
||||
import (
|
||||
"Embalming_Girl_Server/db"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
const AdminPassword = "admin123"
|
||||
|
||||
func Register(mux *http.ServeMux, getStats func() map[string]interface{}) {
|
||||
mux.HandleFunc("/admin", func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Content-Type", "text/html; charset=utf-8")
|
||||
fmt.Fprint(w, adminHTML)
|
||||
})
|
||||
mux.HandleFunc("/api/admin/login", func(w http.ResponseWriter, r *http.Request) {
|
||||
var req struct {
|
||||
Password string `json:"password"`
|
||||
}
|
||||
json.NewDecoder(r.Body).Decode(&req)
|
||||
if req.Password != AdminPassword {
|
||||
json.NewEncoder(w).Encode(map[string]interface{}{"ok": false})
|
||||
return
|
||||
}
|
||||
json.NewEncoder(w).Encode(map[string]interface{}{"ok": true})
|
||||
})
|
||||
mux.HandleFunc("/api/admin/stats", func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
json.NewEncoder(w).Encode(getStats())
|
||||
})
|
||||
mux.HandleFunc("/api/admin/users", func(w http.ResponseWriter, r *http.Request) {
|
||||
rows, err := db.DB.Query("SELECT id, username, nickname, bio, avatar, created_at, last_login FROM users ORDER BY id DESC")
|
||||
if err != nil {
|
||||
json.NewEncoder(w).Encode([]int{})
|
||||
return
|
||||
}
|
||||
defer rows.Close()
|
||||
var users []map[string]interface{}
|
||||
for rows.Next() {
|
||||
var id int64
|
||||
var username, nickname, bio, avatar string
|
||||
var created, login interface{}
|
||||
rows.Scan(&id, &username, &nickname, &bio, &avatar, &created, &login)
|
||||
users = append(users, map[string]interface{}{
|
||||
"id": id, "username": username, "nickname": nickname,
|
||||
"bio": bio, "avatar": avatar, "created_at": created, "last_login": login,
|
||||
})
|
||||
}
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
json.NewEncoder(w).Encode(users)
|
||||
})
|
||||
mux.HandleFunc("/api/admin/delete_user", func(w http.ResponseWriter, r *http.Request) {
|
||||
var req struct {
|
||||
ID int64 `json:"id"`
|
||||
}
|
||||
json.NewDecoder(r.Body).Decode(&req)
|
||||
db.DB.Exec("DELETE FROM sessions WHERE user_id=?", req.ID)
|
||||
db.DB.Exec("DELETE FROM users WHERE id=?", req.ID)
|
||||
json.NewEncoder(w).Encode(map[string]interface{}{"ok": true})
|
||||
})
|
||||
}
|
||||
|
||||
const adminHTML = `<!DOCTYPE html><html><head><meta charset="utf-8"><title>管理面板</title>
|
||||
<style>
|
||||
*{margin:0;padding:0;box-sizing:border-box}
|
||||
body{background:#1a1f2e;color:#e0e0e0;font-family:system-ui;padding:20px}
|
||||
h1{color:#5eb3e6;margin-bottom:20px;text-align:center}
|
||||
.card{background:#252b3d;border-radius:12px;padding:20px;margin-bottom:16px}
|
||||
.card h2{color:#d4c5a3;margin-bottom:12px;font-size:18px}
|
||||
.stats{display:flex;gap:16px;flex-wrap:wrap}
|
||||
.stat{background:#1a1f2e;border-radius:8px;padding:16px;text-align:center;min-width:120px}
|
||||
.stat .num{font-size:28px;color:#5eb3e6;font-weight:bold}
|
||||
.stat .label{font-size:12px;color:#8a8f9d;margin-top:4px}
|
||||
table{width:100%;border-collapse:collapse;margin-top:8px}
|
||||
th,td{padding:8px 12px;text-align:left;border-bottom:1px solid #2a3040}
|
||||
th{color:#8a8f9d;font-size:12px}
|
||||
.btn{padding:4px 12px;border:none;border-radius:4px;cursor:pointer;font-size:12px}
|
||||
.btn-danger{background:#d94f5c;color:white}
|
||||
.btn-danger:hover{background:#c0392b}
|
||||
#loginBox{max-width:300px;margin:100px auto;text-align:center}
|
||||
#loginBox input{width:100%;padding:10px;margin:8px 0;border-radius:6px;border:1px solid #3a3f4e;background:#1a1f2e;color:white}
|
||||
#loginBox button{width:100%;padding:10px;background:#5eb3e6;border:none;border-radius:6px;color:white;cursor:pointer;font-size:14px}
|
||||
#main{display:none}
|
||||
</style></head><body>
|
||||
<div id="loginBox"><h1>管理面板</h1><input id="pw" type="password" placeholder="管理密码">
|
||||
<button onclick="doLogin()">登录</button></div>
|
||||
<div id="main"><h1>Embalming Girl 管理面板</h1>
|
||||
<div class="card"><h2>服务器状态</h2><div class="stats" id="stats"></div></div>
|
||||
<div class="card"><h2>注册用户</h2><table><thead><tr><th>ID</th><th>用户名</th><th>昵称</th><th>头像</th><th>简介</th><th>注册时间</th><th>操作</th></tr></thead><tbody id="users"></tbody></table></div>
|
||||
</div>
|
||||
<script>
|
||||
async function doLogin(){
|
||||
let r=await fetch('/api/admin/login',{method:'POST',body:JSON.stringify({password:document.getElementById('pw').value})});
|
||||
let d=await r.json();
|
||||
if(d.ok){document.getElementById('loginBox').style.display='none';document.getElementById('main').style.display='block';loadData();}
|
||||
else alert('密码错误');
|
||||
}
|
||||
async function loadData(){
|
||||
let s=await(await fetch('/api/admin/stats')).json();
|
||||
document.getElementById('stats').innerHTML=
|
||||
'<div class="stat"><div class="num">'+s.total_users+'</div><div class="label">注册用户</div></div>'+
|
||||
'<div class="stat"><div class="num">'+s.active_rooms+'</div><div class="label">活跃房间</div></div>'+
|
||||
'<div class="stat"><div class="num">'+s.online_clients+'</div><div class="label">在线连接</div></div>';
|
||||
let u=await(await fetch('/api/admin/users')).json();
|
||||
let h='';(u||[]).forEach(x=>{
|
||||
h+='<tr><td>'+x.id+'</td><td>'+x.username+'</td><td>'+x.nickname+'</td><td>'+
|
||||
(x.avatar||'-')+'</td><td>'+(x.bio||'-')+'</td><td>'+(x.created_at||'-')+'</td>'+
|
||||
'<td><button class="btn btn-danger" onclick="delUser('+x.id+')">删除</button></td></tr>';
|
||||
});
|
||||
document.getElementById('users').innerHTML=h||'<tr><td colspan="7">暂无用户</td></tr>';
|
||||
}
|
||||
async function delUser(id){
|
||||
if(!confirm('确定删除此用户?'))return;
|
||||
await fetch('/api/admin/delete_user',{method:'POST',body:JSON.stringify({id})});
|
||||
loadData();
|
||||
}
|
||||
</script></body></html>`
|
||||
Reference in New Issue
Block a user