1、reate limit。
This commit is contained in:
parent
5326172a99
commit
92e97d8fb8
@ -20,6 +20,8 @@ func LoginPage(c *gin.Context) {
|
|||||||
|
|
||||||
func UserSignInHandler(jwtC *jwt.Jwt) func(c *gin.Context) error {
|
func UserSignInHandler(jwtC *jwt.Jwt) func(c *gin.Context) error {
|
||||||
return func(c *gin.Context) error {
|
return func(c *gin.Context) error {
|
||||||
|
log, _ := utils.GetLogFromContext(c)
|
||||||
|
|
||||||
var user models.UserInfoParams
|
var user models.UserInfoParams
|
||||||
|
|
||||||
if err := c.ShouldBindJSON(&user); err != nil {
|
if err := c.ShouldBindJSON(&user); err != nil {
|
||||||
@ -39,10 +41,10 @@ func UserSignInHandler(jwtC *jwt.Jwt) func(c *gin.Context) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Println(tokenStr)
|
|
||||||
|
|
||||||
token := fmt.Sprintf("%s%s", models.GinAuthorPrefixKey, tokenStr)
|
token := fmt.Sprintf("%s%s", models.GinAuthorPrefixKey, tokenStr)
|
||||||
|
|
||||||
|
log.Sugar().Infof("Set token: %s", token)
|
||||||
|
|
||||||
c.SetCookie(models.GinAuthorKey, token, 24*60*60, "/", "", false, true)
|
c.SetCookie(models.GinAuthorKey, token, 24*60*60, "/", "", false, true)
|
||||||
|
|
||||||
c.JSON(http.StatusOK, gin.H{"message": "Login successful"})
|
c.JSON(http.StatusOK, gin.H{"message": "Login successful"})
|
||||||
@ -55,6 +57,8 @@ func UserLogOutHandler(c *gin.Context) {
|
|||||||
log, _ := utils.GetLogFromContext(c)
|
log, _ := utils.GetLogFromContext(c)
|
||||||
|
|
||||||
c.SetCookie(models.GinAuthorKey, "", -1, "/", "", false, true)
|
c.SetCookie(models.GinAuthorKey, "", -1, "/", "", false, true)
|
||||||
|
|
||||||
log.Sugar().Info("Logout successful")
|
log.Sugar().Info("Logout successful")
|
||||||
|
|
||||||
c.JSON(http.StatusOK, gin.H{"message": "Logout successful"})
|
c.JSON(http.StatusOK, gin.H{"message": "Logout successful"})
|
||||||
}
|
}
|
||||||
|
@ -45,7 +45,7 @@ func FileUploadHandle(c *gin.Context) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 限制文件大小(10MB)
|
// 限制文件大小(10MB)
|
||||||
const maxFileSize = 10 << 20 // 10MB
|
const maxFileSize = 100 << 20 // 100MB
|
||||||
if file.Size > maxFileSize {
|
if file.Size > maxFileSize {
|
||||||
log.Sugar().Errorf("File too large: %d bytes", file.Size)
|
log.Sugar().Errorf("File too large: %d bytes", file.Size)
|
||||||
return errors.New("File size exceeds 10MB")
|
return errors.New("File size exceeds 10MB")
|
||||||
@ -64,7 +64,8 @@ func FileUploadHandle(c *gin.Context) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Sugar().Debug("File uploaded successfully to %s", targetPath)
|
log.Sugar().Debugf("File uploaded successfully to %s", targetPath)
|
||||||
|
|
||||||
c.JSON(http.StatusOK, gin.H{"message": fmt.Sprintf("File uploaded to %s", targetPath)})
|
c.JSON(http.StatusOK, gin.H{"message": fmt.Sprintf("File uploaded to %s", targetPath)})
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
@ -114,7 +115,8 @@ func FileListHandle(c *gin.Context) error {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Sugar().Debug("Listed files in %s: %d entries", cleanPath, len(files))
|
log.Sugar().Debugf("Listed files in %s: %d entries", cleanPath, len(files))
|
||||||
|
|
||||||
c.JSON(http.StatusOK, gin.H{"files": files})
|
c.JSON(http.StatusOK, gin.H{"files": files})
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
@ -151,7 +153,7 @@ func FileDownloadHandle(c *gin.Context) error {
|
|||||||
|
|
||||||
if !fileInfo.IsDir() {
|
if !fileInfo.IsDir() {
|
||||||
// 处理文件下载
|
// 处理文件下载
|
||||||
const maxFileSize = 10 << 20 // 10MB
|
const maxFileSize = 100 << 20 // 100MB
|
||||||
if fileInfo.Size() > maxFileSize {
|
if fileInfo.Size() > maxFileSize {
|
||||||
log.Sugar().Errorf("File too large: %d bytes", fileInfo.Size())
|
log.Sugar().Errorf("File too large: %d bytes", fileInfo.Size())
|
||||||
return errors.New("File size exceeds 10MB")
|
return errors.New("File size exceeds 10MB")
|
||||||
@ -160,6 +162,7 @@ func FileDownloadHandle(c *gin.Context) error {
|
|||||||
c.Header("Content-Disposition", fmt.Sprintf("attachment; filename=%q", filepath.Base(cleanPath)))
|
c.Header("Content-Disposition", fmt.Sprintf("attachment; filename=%q", filepath.Base(cleanPath)))
|
||||||
c.Header("Content-Type", "application/octet-stream")
|
c.Header("Content-Type", "application/octet-stream")
|
||||||
c.File(cleanPath)
|
c.File(cleanPath)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -173,7 +176,7 @@ func FileDownloadHandle(c *gin.Context) error {
|
|||||||
|
|
||||||
zipWriter := zip.NewWriter(tmpFile)
|
zipWriter := zip.NewWriter(tmpFile)
|
||||||
totalSize := int64(0)
|
totalSize := int64(0)
|
||||||
const maxZipSize = 10 << 20 // 10MB
|
const maxZipSize = 100 << 20 // 100MB
|
||||||
|
|
||||||
err = filepath.Walk(cleanPath, func(path string, info os.FileInfo, err error) error {
|
err = filepath.Walk(cleanPath, func(path string, info os.FileInfo, err error) error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -199,7 +202,7 @@ func FileDownloadHandle(c *gin.Context) error {
|
|||||||
header.Method = zip.Deflate
|
header.Method = zip.Deflate
|
||||||
totalSize += info.Size()
|
totalSize += info.Size()
|
||||||
if totalSize > maxZipSize {
|
if totalSize > maxZipSize {
|
||||||
return fmt.Errorf("directory size exceeds 10MB")
|
return fmt.Errorf("directory size exceeds 100MB")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -238,7 +241,8 @@ func FileDownloadHandle(c *gin.Context) error {
|
|||||||
c.Header("Content-Disposition", fmt.Sprintf("attachment; filename=%q", filepath.Base(cleanPath)+".zip"))
|
c.Header("Content-Disposition", fmt.Sprintf("attachment; filename=%q", filepath.Base(cleanPath)+".zip"))
|
||||||
c.Header("Content-Type", "application/zip")
|
c.Header("Content-Type", "application/zip")
|
||||||
c.File(tmpFile.Name())
|
c.File(tmpFile.Name())
|
||||||
log.Sugar().Debug("Directory %s downloaded as zip", cleanPath)
|
|
||||||
|
log.Sugar().Debugf("Directory %s downloaded as zip", cleanPath)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -133,15 +133,17 @@ func GinJwtAuthor(jwtC *jwt.Jwt) appHandler {
|
|||||||
|
|
||||||
func GinRateLimit(rateC settings.RateLimitConfig) appHandler {
|
func GinRateLimit(rateC settings.RateLimitConfig) appHandler {
|
||||||
lrate, err := rate.New(rate.WithCapacity(rateC.Capacity),
|
lrate, err := rate.New(rate.WithCapacity(rateC.Capacity),
|
||||||
rate.WithFillInterval(time.Duration(rateC.FillInterval)),
|
rate.WithFillInterval(time.Duration(rateC.FillInterval)*time.Millisecond),
|
||||||
)
|
)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil || lrate.Bucket == nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return func(c *gin.Context) error {
|
return func(c *gin.Context) error {
|
||||||
if lrate.Available() ==0 {
|
if !lrate.WaitMaxDuration(1, time.Duration(rateC.MaxWait)*time.Second) {
|
||||||
|
time.Sleep(time.Duration(rateC.MaxWait) * time.Second)
|
||||||
|
c.Abort()
|
||||||
return &models.BaseError{
|
return &models.BaseError{
|
||||||
Code: http.StatusServiceUnavailable,
|
Code: http.StatusServiceUnavailable,
|
||||||
Msg: "Exceeded rate limit",
|
Msg: "Exceeded rate limit",
|
||||||
|
@ -21,7 +21,7 @@ func errWapper(appH appHandler) gin.HandlerFunc {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
var baseErr *models.BaseError
|
var baseErr *models.BaseError
|
||||||
if errors.As(err, &baseErr) {
|
if errors.As(err, &baseErr) {
|
||||||
log.Error("Base error", zap.Any("res", baseErr))
|
log.Error("Base error", zap.Any("baseErr", baseErr))
|
||||||
|
|
||||||
c.JSON(http.StatusBadRequest, baseErr)
|
c.JSON(http.StatusBadRequest, baseErr)
|
||||||
|
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
<title>Linux 系统运维监控</title>
|
<title>Avcnet 系统运维监控</title>
|
||||||
<script src="https://cdn.tailwindcss.com"></script>
|
<script src="https://cdn.tailwindcss.com"></script>
|
||||||
<script src="https://unpkg.com/xterm@5.3.0/lib/xterm.js"></script>
|
<script src="https://unpkg.com/xterm@5.3.0/lib/xterm.js"></script>
|
||||||
<link href="https://unpkg.com/xterm@5.3.0/css/xterm.css" rel="stylesheet" />
|
<link href="https://unpkg.com/xterm@5.3.0/css/xterm.css" rel="stylesheet" />
|
||||||
@ -28,7 +28,7 @@
|
|||||||
</head>
|
</head>
|
||||||
<body class="bg-gray-100 font-sans">
|
<body class="bg-gray-100 font-sans">
|
||||||
<div class="container mx-auto p-4">
|
<div class="container mx-auto p-4">
|
||||||
<h1 class="text-2xl font-bold mb-4">Linux 系统运维监控</h1>
|
<h1 class="text-2xl font-bold mb-4">Avcnet 系统运维监控</h1>
|
||||||
<button id="logout" class="bg-red-500 text-white px-4 py-2 rounded mb-4">登出</button>
|
<button id="logout" class="bg-red-500 text-white px-4 py-2 rounded mb-4">登出</button>
|
||||||
|
|
||||||
<!-- 系统信息 -->
|
<!-- 系统信息 -->
|
||||||
@ -63,7 +63,7 @@
|
|||||||
<input id="target-dir" type="text" class="w-full px-3 py-2 border rounded" placeholder="请输入目标目录">
|
<input id="target-dir" type="text" class="w-full px-3 py-2 border rounded" placeholder="请输入目标目录">
|
||||||
</div>
|
</div>
|
||||||
<div class="mb-4">
|
<div class="mb-4">
|
||||||
<label class="block text-gray-700 mb-2" for="file">选择文件(最大10MB)</label>
|
<label class="block text-gray-700 mb-2" for="file">选择文件(最大100MB)</label>
|
||||||
<input id="file" type="file" class="w-full px-3 py-2 border rounded">
|
<input id="file" type="file" class="w-full px-3 py-2 border rounded">
|
||||||
</div>
|
</div>
|
||||||
<button id="upload" class="bg-blue-500 text-white px-4 py-2 rounded hover:bg-blue-600">上传</button>
|
<button id="upload" class="bg-blue-500 text-white px-4 py-2 rounded hover:bg-blue-600">上传</button>
|
||||||
@ -137,6 +137,11 @@
|
|||||||
const tooltip = document.getElementById(tooltipId);
|
const tooltip = document.getElementById(tooltipId);
|
||||||
const systemInfo = window.lastSystemInfo || {};
|
const systemInfo = window.lastSystemInfo || {};
|
||||||
|
|
||||||
|
// 如果提示框已显示,不重新定位
|
||||||
|
if (tooltip.style.display === 'block') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
let content = '';
|
let content = '';
|
||||||
if (tooltipId === 'cpu-tooltip') {
|
if (tooltipId === 'cpu-tooltip') {
|
||||||
content = `
|
content = `
|
||||||
@ -167,9 +172,9 @@
|
|||||||
tooltip.innerHTML = content;
|
tooltip.innerHTML = content;
|
||||||
tooltip.style.display = 'block';
|
tooltip.style.display = 'block';
|
||||||
|
|
||||||
// 使用鼠标坐标定位
|
// 使用鼠标坐标定位(仅计算一次)
|
||||||
const offsetX = 10; // 水平偏移
|
const offsetX = 5; // 水平偏移(更近)
|
||||||
const offsetY = 10; // 垂直偏移
|
const offsetY = 5; // 垂直偏移(更近)
|
||||||
let x = event.clientX + window.scrollX + offsetX;
|
let x = event.clientX + window.scrollX + offsetX;
|
||||||
let y = event.clientY + window.scrollY + offsetY;
|
let y = event.clientY + window.scrollY + offsetY;
|
||||||
|
|
||||||
@ -385,6 +390,15 @@
|
|||||||
listFiles(parentPath);
|
listFiles(parentPath);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// 设置默认地址
|
||||||
|
window.onload = function() {
|
||||||
|
const defaultPath = '/tmp/uploads';
|
||||||
|
const defaultListPath = '/app/avcnet';
|
||||||
|
document.getElementById('target-dir').value = defaultPath; // 上传地址默认值
|
||||||
|
document.getElementById('browse-dir').value = defaultListPath; // 浏览地址默认值
|
||||||
|
listFiles(defaultListPath); // 自动加载默认路径文件列表
|
||||||
|
};
|
||||||
|
|
||||||
// 初始检查登录状态并定期更新系统信息
|
// 初始检查登录状态并定期更新系统信息
|
||||||
checkLoginStatus();
|
checkLoginStatus();
|
||||||
updateSystemInfo();
|
updateSystemInfo();
|
||||||
|
Loading…
Reference in New Issue
Block a user