119 lines
5.3 KiB
Go
119 lines
5.3 KiB
Go
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>`
|