Files
labtorary_management_system/backend/main.go
2025-02-19 15:01:38 +08:00

414 lines
16 KiB
Go

package main
import (
"encoding/json"
"github.com/gin-contrib/cors"
"github.com/gin-gonic/gin"
"log"
"net/http"
"os"
"path/filepath"
"time"
"user-management/auth"
"user-management/system"
)
func main() {
// 初始化認證服務
dbPath := os.Getenv("DB_PATH")
if dbPath == "" {
dbPath = "/data/admin.db"
}
authService, err := auth.NewAuthService(dbPath)
if err != nil {
log.Fatal(err)
}
// 獲取 socket 路徑
socketPath := os.Getenv("SOCKET_PATH")
if socketPath == "" {
socketPath = "/var/run/usermgmt.sock"
}
// 創建系統客戶端
client := system.NewSystemClient(socketPath)
r := gin.Default()
// CORS 中間件
// Apply the CORS middleware
r.Use(cors.New(cors.Config{
AllowOrigins: []string{"*"}, // Allow only frontend origin
AllowMethods: []string{"GET", "POST", "PUT", "DELETE", "OPTIONS"},
AllowHeaders: []string{"Content-Type", "Authorization"},
ExposeHeaders: []string{"Content-Length"},
AllowCredentials: true,
MaxAge: 12 * time.Hour,
}))
r.Static("/static", "./markdowns")
// Dynamic endpoint to serve markdown content
r.GET("/api/markdowns/:filename", func(c *gin.Context) {
filename := c.Param("filename")
filePath := filepath.Join("markdowns", filename)
// Read the markdown file
content, err := os.ReadFile(filePath)
if err != nil {
// Handle file not found or read error
c.JSON(http.StatusNotFound, gin.H{"error": "File not found"})
return
}
// Serve the file content
c.Data(http.StatusOK, "text/markdown; charset=utf-8", content)
})
// 管理員認證 API
r.POST("/api/admin/login", func(c *gin.Context) {
var req struct {
Username string `json:"username"`
Password string `json:"password"`
}
if err := c.BindJSON(&req); err != nil {
c.JSON(400, gin.H{"error": err.Error()})
return
}
admin, err := authService.ValidateAdmin(req.Username, req.Password)
if err != nil {
c.JSON(401, gin.H{"error": "Invalid credentials"})
return
}
token, err := auth.GenerateToken(admin.Username)
if err != nil {
c.JSON(500, gin.H{"error": "Failed to generate token"})
return
}
c.JSON(200, gin.H{
"token": token,
"admin": admin,
})
})
// 管理員 CRUD API
adminAPI := r.Group("/api/admin")
adminAPI.GET("/admins", func(c *gin.Context) {
admins, err := authService.GetAdmins()
if err != nil {
c.JSON(500, gin.H{"error": err.Error()})
return
}
c.JSON(200, admins)
})
adminAPI.POST("/admins", func(c *gin.Context) {
var req struct {
Username string `json:"username"`
Password string `json:"password"`
}
if err := c.BindJSON(&req); err != nil {
c.JSON(400, gin.H{"error": err.Error()})
return
}
err := authService.CreateAdmin(req.Username, req.Password)
if err != nil {
c.JSON(500, gin.H{"error": err.Error()})
return
}
c.JSON(200, gin.H{"message": "Admin created successfully"})
})
adminAPI.Use(auth.JWTAuthMiddleware())
{
adminAPI.PUT("/admins/:id/password", func(c *gin.Context) {
id := c.Param("id")
var req struct {
Password string `json:"password"`
}
if err := c.BindJSON(&req); err != nil {
c.JSON(400, gin.H{"error": err.Error()})
return
}
err := authService.UpdateAdminPassword(id, req.Password)
if err != nil {
c.JSON(500, gin.H{"error": err.Error()})
return
}
c.JSON(200, gin.H{"message": "Password updated successfully"})
})
adminAPI.PUT("/admins/:id/active", func(c *gin.Context) {
id := c.Param("id")
var req struct {
IsActive bool `json:"is_active"`
}
if err := c.BindJSON(&req); err != nil {
c.JSON(400, gin.H{"error": err.Error()})
return
}
err := authService.UpdateAdminActivate(id, req.IsActive)
if err != nil {
c.JSON(500, gin.H{"error": err.Error()})
return
}
c.JSON(200, gin.H{"message": "Admin active status updated successfully"})
})
adminAPI.DELETE("/admins/:id", func(c *gin.Context) {
id := c.Param("id")
err := authService.DeleteAdmin(id)
if err != nil {
c.JSON(500, gin.H{"error": err.Error()})
return
}
c.JSON(200, gin.H{"message": "Admin deleted successfully"})
})
}
sambaConfigAPI := r.Group("/api/samba/config")
sambaConfigAPI.Use(auth.JWTAuthMiddleware())
{
sambaConfigAPI.GET("/global_setting", func(c *gin.Context) {
settings, err := client.GetSambaGlobalSetting()
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
}
// Unmarshal the JSON string into a map
var settingsMap map[string]interface{}
if err := json.Unmarshal([]byte(settings), &settingsMap); err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to parse settings JSON"})
return
}
c.JSON(http.StatusOK, settingsMap["global"])
})
sambaConfigAPI.GET("/section_setting", func(c *gin.Context) {
settings, err := client.GetSambaSectionSetting()
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
}
// Unmarshal the JSON string into a map
var settingsMap map[string]interface{}
if err := json.Unmarshal([]byte(settings), &settingsMap); err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to parse settings JSON"})
return
}
c.JSON(http.StatusOK, settingsMap["sections"])
})
sambaConfigAPI.POST("/update_section_setting", func(c *gin.Context) {
// Read the request body
var reqBody struct {
SectionSettings string `json:"section_settings"` // Expecting a JSON string
}
if err := c.BindJSON(&reqBody); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid request body"})
return
}
// Call the UpdateSambaSectionSetting function
err := client.UpdateSambaSectionSetting(reqBody.SectionSettings)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
}
c.JSON(http.StatusOK, gin.H{"message": "Section settings updated successfully"})
})
sambaConfigAPI.POST("/update_global_setting", func(c *gin.Context) {
// Read the request body
var reqBody struct {
GlobalSettings string `json:"global_settings"` // Expecting a JSON string
}
if err := c.BindJSON(&reqBody); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid request body"})
return
}
// Call the UpdateSambaGlobalSetting function
err := client.UpdateSambaGlobalSetting(reqBody.GlobalSettings)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
}
c.JSON(http.StatusOK, gin.H{"message": "Global settings updated successfully"})
})
sambaConfigAPI.GET("/status", func(c *gin.Context) {
_, err := client.GetSambaServiceInfo()
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
}
c.JSON(http.StatusOK, "{\"timestamp\": \"2025-01-26T20:21:50.949817+0800\", \"version\": \"4.19.5-Ubuntu\", \"smb_conf\": \"/etc/samba/smb.conf\", \"sessions\": {\"4116888046\": {\"session_id\": \"4116888046\", \"server_id\": {\"pid\": \"1805949\", \"task_id\": \"0\", \"vnn\": \"4294967295\", \"unique_id\": \"469723148854225877\"}, \"uid\": 1001, \"gid\": 1001, \"username\": \"jing\", \"groupname\": \"jing\", \"remote_machine\": \"192.168.0.1\", \"hostname\": \"ipv4:192.168.0.1:60343\", \"session_dialect\": \"SMB3_11\", \"encryption\": {\"cipher\": \"-\", \"degree\": \"none\"}, \"signing\": {\"cipher\": \"AES-128-GMAC\", \"degree\": \"partial\"}}}, \"tcons\": {\"2344016500\": {\"service\": \"share_data\", \"server_id\": {\"pid\": \"1805949\", \"task_id\": \"0\", \"vnn\": \"4294967295\", \"unique_id\": \"469723148854225877\"}, \"tcon_id\": \"2344016500\", \"session_id\": \"4116888046\", \"machine\": \"192.168.0.1\", \"connected_at\": \"2025-01-26T17:11:07.474663+08:00\", \"encryption\": {\"cipher\": \"-\", \"degree\": \"none\"}, \"signing\": {\"cipher\": \"-\", \"degree\": \"none\"}}, \"3876116464\": {\"service\": \"public_data\", \"server_id\": {\"pid\": \"1805949\", \"task_id\": \"0\", \"vnn\": \"4294967295\", \"unique_id\": \"469723148854225877\"}, \"tcon_id\": \"3876116464\", \"session_id\": \"4116888046\", \"machine\": \"192.168.0.1\", \"connected_at\": \"2025-01-26T17:11:07.477214+08:00\", \"encryption\": {\"cipher\": \"-\", \"degree\": \"none\"}, \"signing\": {\"cipher\": \"-\", \"degree\": \"none\"}}}, \"open_files\": {\"/samba/share_data/.\": {\"service_path\": \"/samba/share_data\", \"filename\": \".\", \"fileid\": {\"devid\": 2050, \"inode\": 43646978, \"extid\": 0}, \"num_pending_deletes\": 0, \"opens\": {\"1805949/39\": {\"server_id\": {\"pid\": \"1805949\", \"task_id\": \"0\", \"vnn\": \"4294967295\", \"unique_id\": \"469723148854225877\"}, \"uid\": 65534, \"share_file_id\": \"39\", \"sharemode\": {\"hex\": \"0x00000007\", \"READ\": true, \"WRITE\": true, \"DELETE\": true, \"text\": \"RWD\"}, \"access_mask\": {\"hex\": \"0x00100081\", \"READ_DATA\": true, \"WRITE_DATA\": false, \"APPEND_DATA\": false, \"READ_EA\": false, \"WRITE_EA\": false, \"EXECUTE\": false, \"READ_ATTRIBUTES\": true, \"WRITE_ATTRIBUTES\": false, \"DELETE_CHILD\": false, \"DELETE\": false, \"READ_CONTROL\": false, \"WRITE_DAC\": false, \"SYNCHRONIZE\": true, \"ACCESS_SYSTEM_SECURITY\": false, \"text\": \"R\"}, \"caching\": {\"READ\": false, \"WRITE\": false, \"HANDLE\": false, \"hex\": \"0x00000000\", \"text\": \"\"}, \"oplock\": {}, \"lease\": {}, \"opened_at\": \"2025-01-26T17:11:08.520410+08:00\"}, \"1805949/27\": {\"server_id\": {\"pid\": \"1805949\", \"task_id\": \"0\", \"vnn\": \"4294967295\", \"unique_id\": \"469723148854225877\"}, \"uid\": 65534, \"share_file_id\": \"27\", \"sharemode\": {\"hex\": \"0x00000007\", \"READ\": true, \"WRITE\": true, \"DELETE\": true, \"text\": \"RWD\"}, \"access_mask\": {\"hex\": \"0x00100081\", \"READ_DATA\": true, \"WRITE_DATA\": false, \"APPEND_DATA\": false, \"READ_EA\": false, \"WRITE_EA\": false, \"EXECUTE\": false, \"READ_ATTRIBUTES\": true, \"WRITE_ATTRIBUTES\": false, \"DELETE_CHILD\": false, \"DELETE\": false, \"READ_CONTROL\": false, \"WRITE_DAC\": false, \"SYNCHRONIZE\": true, \"ACCESS_SYSTEM_SECURITY\": false, \"text\": \"R\"}, \"caching\": {\"READ\": false, \"WRITE\": false, \"HANDLE\": false, \"hex\": \"0x00000000\", \"text\": \"\"}, \"oplock\": {}, \"lease\": {}, \"opened_at\": \"2025-01-26T17:11:08.458851+08:00\"}, \"1805949/13\": {\"server_id\": {\"pid\": \"1805949\", \"task_id\": \"0\", \"vnn\": \"4294967295\", \"unique_id\": \"469723148854225877\"}, \"uid\": 65534, \"share_file_id\": \"13\", \"sharemode\": {\"hex\": \"0x00000007\", \"READ\": true, \"WRITE\": true, \"DELETE\": true, \"text\": \"RWD\"}, \"access_mask\": {\"hex\": \"0x00100081\", \"READ_DATA\": true, \"WRITE_DATA\": false, \"APPEND_DATA\": false, \"READ_EA\": false, \"WRITE_EA\": false, \"EXECUTE\": false, \"READ_ATTRIBUTES\": true, \"WRITE_ATTRIBUTES\": false, \"DELETE_CHILD\": false, \"DELETE\": false, \"READ_CONTROL\": false, \"WRITE_DAC\": false, \"SYNCHRONIZE\": true, \"ACCESS_SYSTEM_SECURITY\": false, \"text\": \"R\"}, \"caching\": {\"READ\": false, \"WRITE\": false, \"HANDLE\": false, \"hex\": \"0x00000000\", \"text\": \"\"}, \"oplock\": {}, \"lease\": {}, \"opened_at\": \"2025-01-26T17:11:08.445320+08:00\"}}}, \"/samba/lab_data/.\": {\"service_path\": \"/samba/lab_data\", \"filename\": \".\", \"fileid\": {\"devid\": 2050, \"inode\": 43654374, \"extid\": 0}, \"num_pending_deletes\": 0, \"opens\": {\"1805949/3\": {\"server_id\": {\"pid\": \"1805949\", \"task_id\": \"0\", \"vnn\": \"4294967295\", \"unique_id\": \"469723148854225877\"}, \"uid\": 65534, \"share_file_id\": \"3\", \"sharemode\": {\"hex\": \"0x00000000\", \"READ\": false, \"WRITE\": false, \"DELETE\": false, \"text\": \"\"}, \"access_mask\": {\"hex\": \"0x00100080\", \"READ_DATA\": false, \"WRITE_DATA\": false, \"APPEND_DATA\": false, \"READ_EA\": false, \"WRITE_EA\": false, \"EXECUTE\": false, \"READ_ATTRIBUTES\": true, \"WRITE_ATTRIBUTES\": false, \"DELETE_CHILD\": false, \"DELETE\": false, \"READ_CONTROL\": false, \"WRITE_DAC\": false, \"SYNCHRONIZE\": true, \"ACCESS_SYSTEM_SECURITY\": false, \"text\": \"\"}, \"caching\": {\"READ\": false, \"WRITE\": false, \"HANDLE\": false, \"hex\": \"0x00000000\", \"text\": \"\"}, \"oplock\": {}, \"lease\": {}, \"opened_at\": \"2025-01-26T17:11:07.479460+08:00\"}}}}}\n")
})
sambaConfigAPI.POST("/restart_samba", func(c *gin.Context) {
err := client.RestartSambaService()
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
}
c.JSON(http.StatusOK, gin.H{"message": "Success restart samba service"})
})
}
// 用戶管理 API
r.GET("/api/users", func(c *gin.Context) {
users, err := client.GetUsers()
if err != nil {
c.JSON(500, gin.H{"error": err.Error()})
return
}
c.JSON(200, users)
})
r.POST("/api/users", func(c *gin.Context) {
var req struct {
Username string `json:"username"`
Password string `json:"password"`
Group string `json:"group"`
IsAdmin bool `json:"is_admin"`
Shell string `json:"shell"`
}
if err := c.BindJSON(&req); err != nil {
c.JSON(400, gin.H{"error": err.Error()})
return
}
err := client.CreateUser(req.Username, req.Password, req.Group, req.IsAdmin, req.Shell)
if err != nil {
c.JSON(500, gin.H{"error": err.Error()})
return
}
c.JSON(200, gin.H{"message": "User created successfully"})
})
r.PUT("/api/users/:username", func(c *gin.Context) {
username := c.Param("username")
var req struct {
Password string `json:"password"`
}
if err := c.BindJSON(&req); err != nil {
c.JSON(400, gin.H{"error": err.Error()})
return
}
err := client.ModifyUserPasswd(username, req.Password)
if err != nil {
c.JSON(500, gin.H{"error": err.Error()})
return
}
c.JSON(200, gin.H{"message": "User modified successfully"})
})
r.DELETE("/api/users/:username", func(c *gin.Context) {
username := c.Param("username")
err := client.DeleteUser(username)
if err != nil {
c.JSON(500, gin.H{"error": err.Error()})
return
}
c.JSON(200, gin.H{"message": "User deleted successfully"})
})
r.GET("/api/groups", func(c *gin.Context) {
groups, err := client.GetGroups()
if err != nil {
c.JSON(500, gin.H{"error": err.Error()})
return
}
c.JSON(200, groups)
})
r.GET("/api/groups/:username", func(c *gin.Context) {
username := c.Param("username")
groups, err := client.GetUserGroups(username)
if err != nil {
c.JSON(500, gin.H{"error": err.Error()})
return
}
c.JSON(200, groups)
})
// Add user to multiple groups endpoint
r.POST("/api/groups/:username", func(c *gin.Context) {
username := c.Param("username")
var req struct {
Groups []string `json:"groups"`
}
if err := c.BindJSON(&req); err != nil {
c.JSON(400, gin.H{"error": err.Error()})
return
}
// Add user to each group
err := client.AddUserToGroups(username, req.Groups)
if err != nil {
c.JSON(500, gin.H{"error": err.Error()})
return
}
c.JSON(200, gin.H{"message": "User added to groups successfully"})
})
// Remove user from a group endpoint
r.DELETE("/api/groups/:username", func(c *gin.Context) {
username := c.Param("username")
var req struct {
Group string `json:"group"`
}
if err := c.BindJSON(&req); err != nil {
c.JSON(400, gin.H{"error": err.Error()})
return
}
err := client.RemoveUserFromGroup(username, req.Group)
if err != nil {
c.JSON(500, gin.H{"error": err.Error()})
return
}
c.JSON(200, gin.H{"message": "Group removed successfully"})
})
// Samba 連接 API
r.GET("/api/samba-connections", func(c *gin.Context) {
connections, err := client.GetSambaConnections()
if err != nil {
c.JSON(500, gin.H{"error": err.Error()})
return
}
c.JSON(200, connections)
})
// 啟動服務器
port := os.Getenv("PORT")
if port == "" {
port = "8080"
}
log.Printf("Server starting on port %s", port)
if err := r.Run(":" + port); err != nil {
log.Fatal(err)
}
}