Compare commits

...

43 Commits

Author SHA1 Message Date
xyiege 10d313d2b5 创建用户表结构 2 weeks ago
xyiege f37dce7ddf 创建公私钥 2 weeks ago
xyiege cbf47ac64d 创建用户表 2 weeks ago
xyiege c23dc2d768 创建用户表 2 weeks ago
xyiege 9afd630885 引入登录 3 weeks ago
xyiege 319e9de5df 使用vuex 存储和管理权限 3 weeks ago
xyiege 6ec8879127 删除stores 3 weeks ago
xyiege 46453c6082 代码修改 3 weeks ago
xyiege ba1311ae59 增加登录 3 weeks ago
xyiege 3272734986 增加npress进度条 3 weeks ago
xyiege 6c2d4c890f 增加预检测 3 weeks ago
xyiege 567397947b 抽取css存入独立的文件中 3 weeks ago
xyiege 959edd8642 修改日志文件格式 1 month ago
xyiege 694b49e297 创建文件更新日志 1 month ago
xyiege 88f0b01e45 创建文件更新日志 1 month ago
xyiege b1d7f9c184 修正发送文件路径错误的异常 1 month ago
xyiege 9b961d943c 压缩包路径正确 1 month ago
xyiege 2ec36400c3 修正路径错误 1 month ago
xyiege 30b304ecc7 调整路径错误 1 month ago
xyiege 73b6ebe0e9 调整压缩包存放路径 1 month ago
xyiege 14900be914 刷新列表 1 month ago
xyiege 283d55b27a 提交成功后刷新 1 month ago
xyiege 9ea6a946c5 保持原有文件时间 1 month ago
xyiege e626fdaf32 调整解压文件存在情况 1 month ago
xyiege e75cfb50c1 文件存在的话 先备份 1 month ago
xyiege 4c0b0fbd85 文件压缩时候保留原有的权限和时间 1 month ago
xyiege ae0cae2c69 文件删除 1 month ago
xyiege d17e54be9f 完善弹框 1 month ago
xyiege acccc459df 增加弹框样式 1 month ago
xyiege c8632d04ff 修改解压路径问题 1 month ago
xyiege fe85c08e65 zip解压目录修改动态获取 1 month ago
xyiege a38396397e 解压目录修改为动态获取 1 month ago
xyiege 016fcc9464 增加远程解压 1 month ago
xyiege aa09341384 远程调用解压缩 1 month ago
xyiege 2b8e84e5d1 rpc中远程解压缩功能 1 month ago
xyiege e7ca179564 移除注释 1 month ago
xyiege 3fec0c77e7 上传完成后自动解压缩 1 month ago
xyiege f3166c7d42 去掉数据库连接的调试信息 2 months ago
xyiege a3ed8cac91 增加运行的脚本,可以启停查看状态等 2 months ago
xyiege fbca9d6acd 使用select调整rpc超时请求 2 months ago
xyiege d2bfd5aeca 增加连接失败跳过 2 months ago
xyiege fe205c59db 组件说明 2 months ago
xyiege 261fff085e 增加解压缩的功能 2 months ago
  1. 3
      aufs/.gitignore
  2. BIN
      aufs/aufs
  3. 272
      aufs/core/login.go
  4. 113
      aufs/core/sendzip.go
  5. 33
      aufs/core/sysmonitor.go
  6. 101
      aufs/crypto/example.go
  7. 191
      aufs/crypto/keygen.go
  8. 15
      aufs/db/dbconst.go
  9. 165
      aufs/db/dbuser.go
  10. 9
      aufs/db/servDb.go
  11. 35
      aufs/db/sqliteDb.go
  12. 9
      aufs/go.mod
  13. 10
      aufs/go.sum
  14. 17
      aufs/main.go
  15. 30
      aufs/private.pem
  16. 9
      aufs/public.pem
  17. 7
      scagent/core/FileRpc.go
  18. 29
      scagent/core/UpFile.go
  19. 163
      scagent/run.sh
  20. 60
      scagent/util/fsutil.go
  21. 2
      scalib/build.bat
  22. 16
      scalib/main.go
  23. 14
      scalib/readme.md
  24. 192
      scalib/util/fsutil.go
  25. 30
      vue/afvue/package-lock.json
  26. 5
      vue/afvue/package.json
  27. 9
      vue/afvue/src/App.vue
  28. 9
      vue/afvue/src/api/scinfo.js
  29. 38
      vue/afvue/src/assets/main.css
  30. 213
      vue/afvue/src/assets/sfilecompare.css
  31. 28
      vue/afvue/src/components/LoginLayout.vue
  32. 0
      vue/afvue/src/components/index.less
  33. 76
      vue/afvue/src/components/nprogress.css
  34. 7
      vue/afvue/src/main.js
  35. 46
      vue/afvue/src/permission.js
  36. 7
      vue/afvue/src/router/index.js
  37. 35
      vue/afvue/src/store/index.js
  38. 111
      vue/afvue/src/store/modules/user.js
  39. 16
      vue/afvue/src/stores/counter.js
  40. 145
      vue/afvue/src/views/Login.vue
  41. 301
      vue/afvue/src/views/Sfilecompare.vue
  42. 329
      vue/afvue/yarn.lock

3
aufs/.gitignore

@ -1,4 +1,5 @@
ups.db
aufs
go.sum
go.mod
go.mod
logs/

BIN
aufs/aufs

Binary file not shown.

272
aufs/core/login.go

@ -0,0 +1,272 @@
package core
import (
"encoding/json"
"errors"
"net/http"
"time"
"aufs/crypto"
"aufs/db"
)
// LoginRequest 登录请求结构
type LoginRequest struct {
Username string `json:"username" binding:"required"`
Password string `json:"password" binding:"required"`
}
// LoginResponse 登录响应结构
type LoginResponse struct {
Token string `json:"token"`
User User `json:"user"`
ExpiresAt time.Time `json:"expires_at"`
}
// User 用户数据结构
type User struct {
ID int `json:"id" db:"id"`
Username string `json:"username" db:"username"`
Email string `json:"email" db:"email"`
Role string `json:"role" db:"role"`
CreatedAt time.Time `json:"created_at" db:"created_at"`
UpdatedAt time.Time `json:"updated_at" db:"updated_at"`
LastLogin time.Time `json:"last_login" db:"last_login"`
Status string `json:"status" db:"status"` // active, locked, deleted
}
// 处理登录传入的数据
func LoginHandler(w http.ResponseWriter, r *http.Request) {
// 解析请求体中的JSON数据
var loginReq LoginRequest
err := json.NewDecoder(r.Body).Decode(&loginReq)
if err != nil {
http.Error(w, "Invalid request payload", http.StatusBadRequest)
return
}
// 验证用户名和密码
user, err := authenticateUser(loginReq.Username, loginReq.Password)
if err != nil {
http.Error(w, err.Error(), http.StatusUnauthorized)
return
}
// 生成访问令牌(这里简化处理,实际项目中可能需要使用JWT等)
token := "temp_token_" + time.Now().Format("20060102150405")
expiresAt := time.Now().Add(24 * time.Hour) // 令牌有效期24小时
// 更新用户最后登录时间
err = db.UpdateUserLastLogin(user.ID)
if err != nil {
// 记录错误但不影响登录流程
// 实际项目中应该有日志记录
}
// 构建响应
response := LoginResponse{
Token: token,
User: *user,
ExpiresAt: expiresAt,
}
// 返回JSON响应
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(response)
}
// 用户状态常量
const (
UserStatusActive = "active"
UserStatusLocked = "locked"
UserStatusDeleted = "deleted"
)
// RegisterRequest 注册请求结构
type RegisterRequest struct {
Username string `json:"username" binding:"required"`
Email string `json:"email" binding:"required"`
Password string `json:"password" binding:"required"`
}
// RegisterResponse 注册响应结构
type RegisterResponse struct {
User User `json:"user"`
Message string `json:"message"`
CreatedAt time.Time `json:"created_at"`
}
// RegisterHandler 处理用户注册
func RegisterHandler(w http.ResponseWriter, r *http.Request) {
// 解析请求体中的JSON数据
var registerReq RegisterRequest
err := json.NewDecoder(r.Body).Decode(&registerReq)
if err != nil {
http.Error(w, "Invalid request payload", http.StatusBadRequest)
return
}
// 检查用户名是否已存在
usernameExists, err := db.CheckUsernameExists(registerReq.Username)
if err != nil {
http.Error(w, "Internal server error", http.StatusInternalServerError)
return
}
if usernameExists {
http.Error(w, "Username already exists", http.StatusConflict)
return
}
// 检查邮箱是否已存在
emailExists, err := db.CheckEmailExists(registerReq.Email)
if err != nil {
http.Error(w, "Internal server error", http.StatusInternalServerError)
return
}
if emailExists {
http.Error(w, "Email already exists", http.StatusConflict)
return
}
// 对密码进行哈希处理
hashedPassword, err := crypto.HashPassword(registerReq.Password)
if err != nil {
http.Error(w, "Failed to process password", http.StatusInternalServerError)
return
}
// 创建用户对象
userDB := db.User{
Username: registerReq.Username,
Email: registerReq.Email,
PasswordHash: hashedPassword,
Role: "user", // 默认角色
Status: UserStatusActive,
CreatedAt: time.Now(),
UpdatedAt: time.Now(),
LastLogin: time.Time{}, // 初始为零值
}
// 保存用户到数据库
createdUser, err := db.AddUser(userDB)
if err != nil {
http.Error(w, "Failed to create user", http.StatusInternalServerError)
return
}
// 转换为core包的User对象
user := User{
ID: createdUser.ID,
Username: createdUser.Username,
Email: createdUser.Email,
Role: createdUser.Role,
CreatedAt: createdUser.CreatedAt,
UpdatedAt: createdUser.UpdatedAt,
LastLogin: createdUser.LastLogin,
Status: createdUser.Status,
}
// 构建响应
response := RegisterResponse{
User: user,
Message: "User registered successfully",
CreatedAt: user.CreatedAt,
}
// 返回JSON响应
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusCreated)
json.NewEncoder(w).Encode(response)
}
// ResetPasswordRequest 重置密码请求结构
type ResetPasswordRequest struct {
Username string `json:"username" binding:"required"`
NewPassword string `json:"new_password" binding:"required"`
}
// ResetPasswordResponse 重置密码响应结构
type ResetPasswordResponse struct {
Message string `json:"message"`
UpdatedAt time.Time `json:"updated_at"`
}
// ResetPasswordHandler 处理密码重置
func ResetPasswordHandler(w http.ResponseWriter, r *http.Request) {
// 解析请求体中的JSON数据
var resetReq ResetPasswordRequest
err := json.NewDecoder(r.Body).Decode(&resetReq)
if err != nil {
http.Error(w, "Invalid request payload", http.StatusBadRequest)
return
}
// 检查用户是否存在
userDB, err := db.GetUserByUsername(resetReq.Username)
if err != nil {
http.Error(w, "User not found", http.StatusNotFound)
return
}
// 检查用户状态
if userDB.Status != UserStatusActive {
http.Error(w, "User account is locked or disabled", http.StatusForbidden)
return
}
// 对新密码进行哈希处理
hashedPassword, err := crypto.HashPassword(resetReq.NewPassword)
if err != nil {
http.Error(w, "Failed to process new password", http.StatusInternalServerError)
return
}
// 更新用户密码
updatedAt := time.Now()
err = db.UpdateUserPassword(userDB.ID, hashedPassword, updatedAt)
if err != nil {
http.Error(w, "Failed to update password", http.StatusInternalServerError)
return
}
// 构建响应
response := ResetPasswordResponse{
Message: "Password reset successful",
UpdatedAt: updatedAt,
}
// 返回JSON响应
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(response)
}
// authenticateUser 验证用户身份
func authenticateUser(username, password string) (*User, error) {
// 根据用户名查询用户信息
userDB, err := db.GetUserByUsername(username)
if err != nil {
return nil, errors.New("用户名或密码错误")
}
// 检查用户状态
if userDB.Status != UserStatusActive {
return nil, errors.New("用户账号已被锁定或禁用")
}
// 验证密码
isValid, err := crypto.VerifyPassword(userDB.PasswordHash, password)
if err != nil || !isValid {
return nil, errors.New("用户名或密码错误")
}
// 转换为core包的User对象
return &User{
ID: userDB.ID,
Username: userDB.Username,
Email: userDB.Email,
Role: userDB.Role,
CreatedAt: userDB.CreatedAt,
UpdatedAt: userDB.UpdatedAt,
LastLogin: userDB.LastLogin,
Status: userDB.Status,
}, nil
}

113
aufs/core/sendzip.go

@ -1,14 +1,11 @@
package core
import (
"aufs/config"
"aufs/util"
"encoding/json"
"fmt"
"net"
"net/http"
"net/rpc/jsonrpc"
"path"
"os"
"path/filepath"
"strings"
"time"
@ -70,6 +67,8 @@ func SendZip(w http.ResponseWriter, r *http.Request) {
RemoteAddr: remoteaddr[0],
RemotePath: remotepath[0],
}
// 记录日志
afsLog(zipblock)
// 从serip 切割出ip 和端口
splitip := strings.Split(serip[0], ":")
@ -111,93 +110,31 @@ func SendZip(w http.ResponseWriter, r *http.Request) {
json.NewEncoder(w).Encode(resp)
}
// 将文件打包成压缩包
func SendZip00(w http.ResponseWriter, r *http.Request) {
r.ParseForm()
// 选择文件,并生成zip包
// 文件
zipfarr := r.Form["sfiles"]
// 服务器ip地址
serip := r.Form["serverip"]
if serip[0] == "" {
http.Error(w, "remote server ip is blank!", http.StatusInternalServerError)
return
// 自动升级的日志
func afsLog(zipblock ZipBlock) {
// 目录格式 2025/10
logdir := fmt.Sprintf("%d/%02d", time.Now().Year(), time.Now().Month())
logdir = filepath.Join("logs", logdir)
// 日志文件
logfile := fmt.Sprintf("%v/aufs.log", logdir)
// 检查目录是否存在
if _, err := os.Stat(logdir); os.IsNotExist(err) {
// 不存在就创建
os.MkdirAll(logdir, 0755)
}
tpath := ""
// 选中的路径,可以为空
wtculpath := r.Form["curpath"]
if wtculpath != nil {
tpath = util.Base64dec(wtculpath[0])
// 检查日志文件是否存在
if _, err := os.Stat(logfile); os.IsNotExist(err) {
// 不存在就创建
os.Create(logfile)
}
// 实际路径
realFilePath := filepath.Join(config.G.FilePath, tpath)
// zip 文件名
zpFileName := "BIU_" + time.Now().Format("20060102_150405") + ".zip"
// 创建zip 异步?
taskId := make(chan string)
go func() {
util.CompressToZip(zpFileName, realFilePath, zipfarr)
taskId <- "arcok"
// fmt.Fprintln(w, "create archive:", err)
}()
// go util.CompressToZip(zpFileName, realFilePath, zipfarr)
fmt.Println("archive is createding...")
// 当前运行的目录
// ZIP 文件的实际路径
ziprl := path.Join("./sync_zips/", zpFileName)
// zip 创建成功后
rest := <-taskId
// 有压缩包 才可以操作
if strings.EqualFold(strings.ToLower(rest), "arcok") {
fmt.Println("archive is sending...")
// 创建udp 渠道发送数据
message := fmt.Sprintf("%s%s%s", config.G.DeviceName, "|", "sender")
UdpSendFile(serip[0], ziprl, zpFileName, message, w)
} else {
fmt.Println("archive is not exist!!!")
}
// 执行完 跳转到 首页
// http.Redirect(w, r, "/", http.StatusFound)
}
// udp 模式发送文件
/*
* serip 服务器ip,
* absfilepath 发送文件的实际路径,
* fname 文件名
* message 携带部分信息的消息
* http.ResponseWriter
*/
func UdpSendFile(serip string, absfilepath string, fname string, message string, w http.ResponseWriter) {
// 1、获取udp addr
remoteAddr, err := net.ResolveUDPAddr("udp", serip+":9099")
if err != nil {
return
}
// 2、 监听端口
conn, err := net.DialUDP("udp", nil, remoteAddr)
// 打开日志文件
f, err := os.OpenFile(logfile, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
if err != nil {
return
fmt.Printf("open log file faild %v", err)
}
defer conn.Close()
defer f.Close()
// 写入日志
logfmt := fmt.Sprintf("%v %v %v %v %v\n", time.Now().Format("2006-01-02 15:04:05"), zipblock.Basepath, zipblock.Curpath, zipblock.SelFile, zipblock.RemoteAddr, zipblock.RemotePath)
f.WriteString(logfmt)
// 3、在端口发送数据
// message := fmt.Sprintf("%s%s%s", config.G.DeviceName, "|", "sender")
// 向链接通道发送数据 数据包头
conn.Write([]byte(message))
// 发送文件
go func() {
err := util.SendFiles(absfilepath, fmt.Sprintf("http://%s/rc", remoteAddr))
if err != nil {
fmt.Printf("Send file to %s error: %s\n", remoteAddr, err)
}
}()
}

33
aufs/core/sysmonitor.go

@ -35,33 +35,38 @@ func SysMonitor(w http.ResponseWriter, r *http.Request) {
scslist := db.GetlScList(1)
//
var rsdata Syslist
var sinfo SysInfo
// todo 待优化
for _, sc := range scslist {
// 利用jsonrpc 请求结果
// 构建访问的链接
ser := sc.Addr + ":" + sc.Port
// 链接服务
client, err := jsonrpc.Dial("tcp", ser)
// 发生错误,执行下一个
if err != nil {
log.Fatalf("Dial SysMonitor failed: %v", ser)
return
log.Fatalf("Dial SysMonitor failed: %v\n", err)
}
//
defer client.Close()
// 返回的是pb字符串
var resp string
// 调用远程方法 SysmonitorService.GetSysInfo
client.Call("SysmonitorService.GetSysInfo", nil, &resp)
// 构建json结构的字符串
sinfo := SysInfo{
Addr: sc.Addr,
Port: sc.Port,
Resp: resp,
if client != nil {
// 返回的是pb字符串
var resp string
// 调用远程方法 SysmonitorService.GetSysInfo
client.Call("SysmonitorService.GetSysInfo", nil, &resp)
// 构建json结构的字符串
sinfo = SysInfo{
Addr: sc.Addr,
Port: sc.Port,
Resp: resp,
}
// 添加到rsdata
rsdata.Slist = append(rsdata.Slist, sinfo)
} else {
continue
}
// 添加到rsdata
rsdata.Slist = append(rsdata.Slist, sinfo)
}
// 生成json数据
sysresp := SysResp{

101
aufs/crypto/example.go

@ -0,0 +1,101 @@
package crypto
import (
"fmt"
"os"
)
// 示例:生成密钥对并存入文件
func ExampleGenerateAndSaveKeys() {
// 密钥位数
bits := 2048
// 用户密码(实际应用中应该从用户输入获取)
password := "your_secure_password"
// 密钥文件保存路径
privateKeyPath := "private.pem"
publicKeyPath := "public.pem"
// 生成并保存密钥对
err := GenerateAndSaveKeysWithPassword(bits, password, privateKeyPath, publicKeyPath)
if err != nil {
fmt.Printf("生成密钥失败: %v\n", err)
return
}
fmt.Println("密钥生成和保存成功!")
}
// 示例:读取并解密私钥
func ExampleReadAndDecryptPrivateKey() {
// 密码和文件路径
password := "your_secure_password"
privateKeyPath := "private.pem"
// 读取加密的私钥文件
data, err := os.ReadFile(privateKeyPath)
if err != nil {
fmt.Printf("读取私钥文件失败: %v\n", err)
return
}
// 解密私钥
privateKey, err := DecryptPrivateKey(data, password)
if err != nil {
fmt.Printf("解密私钥失败: %v\n", err)
return
}
fmt.Println("私钥解密成功!")
fmt.Printf("私钥模数长度: %d 位\n", privateKey.N.BitLen())
}
// 主函数示例(如果单独运行这个文件)
func ExampleMain() {
// 生成密钥对
ExampleGenerateAndSaveKeys()
// 读取并解密私钥
ExampleReadAndDecryptPrivateKey()
}
/*
使用说明
1. 生成密钥对
- 调用GenerateAndSaveKeysWithPassword函数
- 参数密钥位数(通常2048或4096)保护私钥的密码私钥保存路径公钥保存路径
2. 读取并解密私钥
- 读取私钥文件
- 调用DecryptPrivateKey函数用密码解密
3. 注意事项
- 密码应足够复杂且安全存储
- 私钥文件应妥善保管建议设置严格的文件权限
- 密钥位数推荐使用2048位以上以保证安全性
示例代码
package main
import (
"fmt"
"your_project/crypto"
)
func main() {
// 生成密钥对
bits := 2048
password := "your_secure_password"
privateKeyPath := "private.pem"
publicKeyPath := "public.pem"
err := crypto.GenerateAndSaveKeysWithPassword(bits, password, privateKeyPath, publicKeyPath)
if err != nil {
fmt.Printf("错误: %v\n", err)
return
}
fmt.Println("密钥生成完成!")
}
*/

191
aufs/crypto/keygen.go

@ -0,0 +1,191 @@
package crypto
import (
"crypto/rand"
"crypto/rsa"
"crypto/x509"
"encoding/pem"
"errors"
"fmt"
"golang.org/x/crypto/bcrypt"
"os"
)
// KeyPair 表示生成的密钥对
type KeyPair struct {
PrivateKey *rsa.PrivateKey
PublicKey *rsa.PublicKey
}
// GenerateRSAKeyPair 生成指定位数的RSA密钥对
func GenerateRSAKeyPair(bits int) (*KeyPair, error) {
// 生成RSA私钥
privateKey, err := rsa.GenerateKey(rand.Reader, bits)
if err != nil {
return nil, fmt.Errorf("生成RSA密钥失败: %v", err)
}
// 验证私钥
err = privateKey.Validate()
if err != nil {
return nil, fmt.Errorf("验证RSA密钥失败: %v", err)
}
// 获取公钥
publicKey := &privateKey.PublicKey
return &KeyPair{
PrivateKey: privateKey,
PublicKey: publicKey,
}, nil
}
// EncryptPrivateKey 使用密码加密私钥
func EncryptPrivateKey(privateKey *rsa.PrivateKey, password string) ([]byte, error) {
if password == "" {
return nil, errors.New("密码不能为空")
}
// 将私钥转换为PKCS#1格式
privDER := x509.MarshalPKCS1PrivateKey(privateKey)
// 使用密码加密私钥
block, err := x509.EncryptPEMBlock(
rand.Reader,
"RSA PRIVATE KEY",
privDER,
[]byte(password),
x509.PEMCipherAES256,
)
if err != nil {
return nil, fmt.Errorf("加密私钥失败: %v", err)
}
// 将加密后的私钥转换为PEM格式
encprivPEM := pem.EncodeToMemory(block)
return encprivPEM, nil
}
// ExportPublicKey 将公钥导出为PEM格式
func ExportPublicKey(publicKey *rsa.PublicKey) ([]byte, error) {
// 将公钥转换为DER格式
pubDER, err := x509.MarshalPKIXPublicKey(publicKey)
if err != nil {
return nil, fmt.Errorf("编码公钥失败: %v", err)
}
// 创建PEM块
block := &pem.Block{
Type: "RSA PUBLIC KEY",
Bytes: pubDER,
}
// 将公钥转换为PEM格式
pubPEM := pem.EncodeToMemory(block)
return pubPEM, nil
}
// HashPassword 对密码进行哈希处理
func HashPassword(password string) (string, error) {
hash, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost)
if err != nil {
return "", err
}
return string(hash), nil
}
// CheckPasswordHash 验证密码是否与哈希值匹配
func CheckPasswordHash(password, hash string) bool {
err := bcrypt.CompareHashAndPassword([]byte(hash), []byte(password))
return err == nil
}
// GenerateKeysWithPassword 生成加密的密钥对,返回公钥和加密后的私钥
func GenerateKeysWithPassword(bits int, password string) (publicKeyPEM []byte, encryptedPrivateKeyPEM []byte, err error) {
// 生成密钥对
keyPair, err := GenerateRSAKeyPair(bits)
if err != nil {
return nil, nil, err
}
// 加密私钥
encryptedPrivateKeyPEM, err = EncryptPrivateKey(keyPair.PrivateKey, password)
if err != nil {
return nil, nil, err
}
// 导出公钥
publicKeyPEM, err = ExportPublicKey(keyPair.PublicKey)
if err != nil {
return nil, nil, err
}
return publicKeyPEM, encryptedPrivateKeyPEM, nil
}
// DecryptPrivateKey 使用密码解密私钥
func DecryptPrivateKey(encryptedPrivateKeyPEM []byte, password string) (*rsa.PrivateKey, error) {
if password == "" {
return nil, errors.New("密码不能为空")
}
// 解码PEM块
block, _ := pem.Decode(encryptedPrivateKeyPEM)
if block == nil {
return nil, errors.New("无法解码PEM块")
}
// 解密私钥
decryptedDER, err := x509.DecryptPEMBlock(block, []byte(password))
if err != nil {
return nil, fmt.Errorf("解密私钥失败: %v", err)
}
// 解析私钥
privateKey, err := x509.ParsePKCS1PrivateKey(decryptedDER)
if err != nil {
return nil, fmt.Errorf("解析私钥失败: %v", err)
}
return privateKey, nil
}
// SaveKeyToFile 将密钥数据保存到指定文件
func SaveKeyToFile(filePath string, keyData []byte) error {
// 写入文件
err := os.WriteFile(filePath, keyData, 0600) // 仅用户可读写权限
if err != nil {
return fmt.Errorf("保存密钥到文件失败: %v", err)
}
fmt.Printf("密钥已成功保存到: %s\n", filePath)
return nil
}
// GenerateAndSaveKeysWithPassword 生成密钥对并用密码保护私钥,然后保存到文件
func GenerateAndSaveKeysWithPassword(bits int, password, privateKeyPath, publicKeyPath string) error {
// 生成密钥对
publicKeyPEM, encryptedPrivateKeyPEM, err := GenerateKeysWithPassword(bits, password)
if err != nil {
return err
}
// 保存公钥到文件
err = SaveKeyToFile(publicKeyPath, publicKeyPEM)
if err != nil {
return err
}
// 保存加密后的私钥到文件
err = SaveKeyToFile(privateKeyPath, encryptedPrivateKeyPEM)
if err != nil {
return err
}
fmt.Println("密钥对已成功生成并保存!")
return nil
}

15
aufs/db/dbconst.go

@ -1,5 +1,7 @@
package db
import "time"
// 定义数据库中用的模型
type StFileInfo struct {
Id string `json:"id"`
@ -17,3 +19,16 @@ type StServerInfo struct {
Token string `json:"token"`
Status int `json:"status"`
}
// 用户信息
type User struct {
ID int `json:"id" db:"id"`
Username string `json:"username" db:"username"`
Email string `json:"email" db:"email"`
Role string `json:"role" db:"role"`
CreatedAt time.Time `json:"created_at" db:"created_at"`
UpdatedAt time.Time `json:"updated_at" db:"updated_at"`
LastLogin time.Time `json:"last_login" db:"last_login"`
Status string `json:"status" db:"status"`
PasswordHash string `json:"-" db:"password_hash"`
}

165
aufs/db/dbuser.go

@ -0,0 +1,165 @@
package db
import (
"database/sql"
"time"
)
// CreateUserTable 创建用户表
func CreateUserTable(db *sql.DB) error {
createTableSQL := `
CREATE TABLE IF NOT EXISTS sc_user (
id INTEGER PRIMARY KEY AUTOINCREMENT,
username VARCHAR(50) NOT NULL UNIQUE,
email VARCHAR(100) NOT NULL UNIQUE,
password_hash VARCHAR(255) NOT NULL,
role VARCHAR(20) NOT NULL DEFAULT 'user',
status VARCHAR(20) NOT NULL DEFAULT 'active',
created_at DATETIME NOT NULL,
updated_at DATETIME NOT NULL,
last_login DATETIME
);`
_, err := db.Exec(createTableSQL)
return err
}
// AddUser 添加新用户
func AddUser(user User) (*User, error) {
// 使用全局数据库连接
conn := GetSqliteDB()
if conn == nil {
return nil, ErrDbNotInit
}
insertSQL := `
INSERT INTO sc_user (username, email, password_hash, role, status, created_at, updated_at, last_login)
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
`
result, err := conn.Exec(insertSQL,
user.Username, user.Email, user.PasswordHash, user.Role,
user.Status, user.CreatedAt, user.UpdatedAt, user.LastLogin,
)
if err != nil {
return nil, err
}
// 获取插入的ID
id, err := result.LastInsertId()
if err != nil {
return nil, err
}
// 设置ID并返回
user.ID = int(id)
return &user, nil
}
// GetUserByUsername 根据用户名获取用户
func GetUserByUsername(username string) (*User, error) {
conn := GetSqliteDB()
if conn == nil {
return nil, ErrDbNotInit
}
querySQL := `
SELECT id, username, email, password_hash, role, status, created_at, updated_at, last_login
FROM sc_user WHERE username = ?
`
var user User
err := conn.QueryRow(querySQL, username).Scan(
&user.ID, &user.Username, &user.Email, &user.PasswordHash,
&user.Role, &user.Status, &user.CreatedAt, &user.UpdatedAt, &user.LastLogin,
)
if err != nil {
return nil, err
}
return &user, nil
}
// GetUserByEmail 根据邮箱获取用户
func GetUserByEmail(email string) (*User, error) {
conn := GetSqliteDB()
if conn == nil {
return nil, ErrDbNotInit
}
querySQL := `
SELECT id, username, email, password_hash, role, status, created_at, updated_at, last_login
FROM sc_user WHERE email = ?
`
var user User
err := conn.QueryRow(querySQL, email).Scan(
&user.ID, &user.Username, &user.Email, &user.PasswordHash,
&user.Role, &user.Status, &user.CreatedAt, &user.UpdatedAt, &user.LastLogin,
)
if err != nil {
return nil, err
}
return &user, nil
}
// CheckUsernameExists 检查用户名是否存在
func CheckUsernameExists(username string) (bool, error) {
conn := GetSqliteDB()
if conn == nil {
return false, ErrDbNotInit
}
querySQL := "SELECT COUNT(*) FROM sc_user WHERE username = ?"
var count int
err := conn.QueryRow(querySQL, username).Scan(&count)
if err != nil {
return false, err
}
return count > 0, nil
}
// CheckEmailExists 检查邮箱是否存在
func CheckEmailExists(email string) (bool, error) {
conn := GetSqliteDB()
if conn == nil {
return false, ErrDbNotInit
}
querySQL := "SELECT COUNT(*) FROM sc_user WHERE email = ?"
var count int
err := conn.QueryRow(querySQL, email).Scan(&count)
if err != nil {
return false, err
}
return count > 0, nil
}
// UpdateUserPassword 更新用户密码
func UpdateUserPassword(userID int, passwordHash string, updatedAt time.Time) error {
conn := GetSqliteDB()
if conn == nil {
return ErrDbNotInit
}
updateSQL := "UPDATE sc_user SET password_hash = ?, updated_at = ? WHERE id = ?"
_, err := conn.Exec(updateSQL, passwordHash, updatedAt, userID)
return err
}
// UpdateUserLastLogin 更新用户最后登录时间
func UpdateUserLastLogin(userID int) error {
conn := GetSqliteDB()
if conn == nil {
return ErrDbNotInit
}
updateSQL := "UPDATE sc_user SET last_login = ? WHERE id = ?"
_, err := conn.Exec(updateSQL, time.Now(), userID)
return err
}

9
aufs/db/servDb.go

@ -9,13 +9,20 @@ import (
func CreateScdb() {
// 建表语句
sts := `
CREATE TABLE sc_server (
CREATE TABLE IF NOT EXISTS sc_server (
id INTEGER PRIMARY KEY NOT NULL PRIMARY KEY AUTOINCREMENT,
scname TEXT NOT NULL,
addr TEXT NOT NULL,
port TEXT NOT NULL,
token TEXT NOT NULL,
status INTEGER NOT NULL
);`
// 系统用户表
sts += `
CREATE TABLE IF NOT EXISTS sc_user (
id INTEGER PRIMARY KEY NOT NULL PRIMARY KEY AUTOINCREMENT,
username TEXT NOT NULL,
password TEXT NOT NULL
);`
// 执行建表语句
_, err = db.Exec(sts)

35
aufs/db/sqliteDb.go

@ -2,6 +2,7 @@ package db
import (
"fmt"
"database/sql"
// 导入包,导入前缀为下划线,则init函数被执行,然后注册驱动。
"github.com/jmoiron/sqlx"
@ -11,6 +12,9 @@ import (
var db *sqlx.DB
var err error
// 数据库错误常量
var ErrDbNotInit = fmt.Errorf("database not initialized")
func Init() {
// fmt.Printf("hey,I am initilize function in SqliteDb\n")s
//
@ -24,6 +28,10 @@ func Init() {
// defer db.Close()
//
connectDB()
// 创建必要的表
CreateTable()
// 创建用户表
createUserTableIfNotExists()
}
// 连接数据库
@ -35,8 +43,31 @@ func connectDB() {
fmt.Printf("Database creation failed: %v\n", err)
return
}
// 连接成功,打印出"database connected:版本号"
fmt.Printf("Database creation successful: %v\n", version)
}
// GetSqliteDB 获取数据库连接
func GetSqliteDB() *sql.DB {
if db == nil {
return nil
}
return db.DB
}
// createUserTableIfNotExists 创建用户表(如果不存在)
func createUserTableIfNotExists() {
sqlDB := GetSqliteDB()
if sqlDB == nil {
fmt.Printf("Failed to get database connection for user table creation\n")
return
}
err := CreateUserTable(sqlDB)
if err != nil {
fmt.Printf("Failed to create user table: %v\n", err)
return
}
fmt.Printf("User table created successfully\n")
}
// 创建数据库表

9
aufs/go.mod

@ -1,11 +1,14 @@
module aufs
go 1.23.0
go 1.24.0
toolchain go1.24.6
require (
github.com/jmoiron/sqlx v1.4.0
github.com/logoove/sqlite v1.37.1
github.com/schollz/progressbar/v3 v3.18.0
golang.org/x/crypto v0.44.0
google.golang.org/protobuf v1.36.7
)
@ -18,8 +21,8 @@ require (
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
github.com/rivo/uniseg v0.4.7 // indirect
golang.org/x/exp v0.0.0-20250620022241-b7579e27df2b // indirect
golang.org/x/sys v0.34.0 // indirect
golang.org/x/term v0.28.0 // indirect
golang.org/x/sys v0.38.0 // indirect
golang.org/x/term v0.37.0 // indirect
modernc.org/libc v1.66.3 // indirect
modernc.org/mathutil v1.7.1 // indirect
modernc.org/memory v1.11.0 // indirect

10
aufs/go.sum

@ -40,6 +40,8 @@ github.com/schollz/progressbar/v3 v3.18.0 h1:uXdoHABRFmNIjUfte/Ex7WtuyVslrw2wVPQ
github.com/schollz/progressbar/v3 v3.18.0/go.mod h1:IsO3lpbaGuzh8zIMzgY3+J8l4C8GjO0Y9S69eFvNsec=
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
golang.org/x/crypto v0.44.0 h1:A97SsFvM3AIwEEmTBiaxPPTYpDC47w720rdiiUvgoAU=
golang.org/x/crypto v0.44.0/go.mod h1:013i+Nw79BMiQiMsOPcVCB5ZIJbYkerPrGnOa00tvmc=
golang.org/x/exp v0.0.0-20250620022241-b7579e27df2b h1:M2rDM6z3Fhozi9O7NWsxAkg/yqS/lQJ6PmkyIV3YP+o=
golang.org/x/exp v0.0.0-20250620022241-b7579e27df2b/go.mod h1:3//PLf8L/X+8b4vuAfHzxeRUl04Adcb341+IGKfnqS8=
golang.org/x/mod v0.25.0 h1:n7a+ZbQKQA/Ysbyb0/6IbB1H/X41mKgbhfv7AfG/44w=
@ -47,10 +49,10 @@ golang.org/x/mod v0.25.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww=
golang.org/x/sync v0.15.0 h1:KWH3jNZsfyT6xfAfKiz6MRNmd46ByHDYaZ7KSkCtdW8=
golang.org/x/sync v0.15.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.34.0 h1:H5Y5sJ2L2JRdyv7ROF1he/lPdvFsd0mJHFw2ThKHxLA=
golang.org/x/sys v0.34.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
golang.org/x/term v0.28.0 h1:/Ts8HFuMR2E6IP/jlo7QVLZHggjKQbhu/7H0LJFr3Gg=
golang.org/x/term v0.28.0/go.mod h1:Sw/lC2IAUZ92udQNf3WodGtn4k/XoLyZoh8v/8uiwek=
golang.org/x/sys v0.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc=
golang.org/x/sys v0.38.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
golang.org/x/term v0.37.0 h1:8EGAD0qCmHYZg6J17DvsMy9/wJ7/D/4pV/wfnld5lTU=
golang.org/x/term v0.37.0/go.mod h1:5pB4lxRNYYVZuTLmy8oR2BH8dflOR+IbTYFD8fi3254=
golang.org/x/tools v0.34.0 h1:qIpSLOxeCYGg9TrcJokLBG4KFA6d795g0xkBkiESGlo=
golang.org/x/tools v0.34.0/go.mod h1:pAP9OwEaY1CAW3HOmg3hLZC5Z0CCmzjAF2UQMSqNARg=
google.golang.org/protobuf v1.36.7 h1:IgrO7UwFQGJdRNXH/sQux4R1Dj1WAKcLElzeeRaXV2A=

17
aufs/main.go

@ -3,6 +3,7 @@ package main
import (
"aufs/config"
"aufs/core"
"aufs/crypto"
"fmt"
"log"
"mime"
@ -97,6 +98,22 @@ func main() {
os.Exit(1)
}
// 初始化加密证书
if flag == "-mc" {
bits := 2048
password := "xc1123"
privateKeyPath := "private.pem"
publicKeyPath := "public.pem"
err := crypto.GenerateAndSaveKeysWithPassword(bits, password, privateKeyPath, publicKeyPath)
if err != nil {
fmt.Printf("错误: %v\n", err)
return
}
fmt.Println("密钥生成完成!")
}
// args 长度大于4 才有意思
if len(args) > 4 {
config.G.Port = args[4]

30
aufs/private.pem

@ -0,0 +1,30 @@
-----BEGIN RSA PRIVATE KEY-----
Proc-Type: 4,ENCRYPTED
DEK-Info: AES-256-CBC,60c8623b1977ed9926f090a559fefbb7
EXkcUgG90PRmUMzWCc9tYhVV/FclX0Kgbnaaj++ssUVLfHqo7ktrQ757VGLuJ2zC
Sr6Gi88jxRJq98i6gCe4EzL0Q1I7pzqlH3bGxervnCtX9nbDiA4UKpDDQ/nsDuBp
fzyTtwx3xu7MXxQX54XADexcfX4kP08h4LJoPJbnRk9dvEI29TUAmwX9vCyCZVec
DE1kYQOLDzJvX4sKMsCiKzjTQwl46LdYHdn3wYzETnmR3/0dIzDLldWt9TZuYue4
/lpUdtVY2DGC2+2y9jvNlos9vpsZ2FyUi+M0NICmtkiPoqXzqhuPewwZ+hCw3rbe
p/Q12NiMcnX+0cWwNqgZjDgwUOM+b8UW4pYNYlFzzhBBFA88EI4QGJLHM2ougTS2
9gXrIOwknRdnvIuUzuf7UIJ7s2C6tIhKQyyMbdzZ2tQQQ7ei+S7EpY6fHJ5OfYfK
5KbjuneZV/8eCoT97KInTKqMpT57cFVNHzLlri+9kXPpY6HAl+L8Qp7IhAjfDnp7
jElgWPaNcuuR4wYJY6ehNBFQ0Hp8SgduquATVf8FssrM4eTyirBsBrMYSbwl3FGZ
H6BuksvCqq31M/k1EscSbGrb99MokMsQTUeNbY438QvukRi6oowXuriqiQwXhgOl
4Xc8AIxeE79dDfPPskbhGWgBxztolrHEpE2Ttdxa/4q95sMHhEqPDtLYri6immqk
WHcly5tlAtA/JiEAxC2dwDL4vkdB0s92+wCXt5HOlkGqXLLkkE9ST+mwpGGwCk/9
BI65vh87ME/dBCPNfNCK7iKU2E1cRjJmpgD+dowCwH4g8QmIj73t1pZtY6XiWdx3
5797R8cA62HItmGw05/Ab9niFkecJYhDWM+N7iqLHadhtTubwCkOIE7PkBjuWsL/
zTj3RlyGxELwokJ6qeC93Zg/OzKGFCdLMHEkD1RIrglYOGBLO1NovFzPHFav8rkB
jEkl4GsQX6OzeImlDOCkx0YH1ron8lydBTY2ohhtwJkn2MDlEa3CYC/yz9IqzzkI
fMBqFpVe/Ehy2ekuRYen5LRBaDG1DhHeCByr00+q7liCqm47bALsiS7O0d8W6mEw
UNr5GkHPCsxq3fk0zw2OuR6txfmCP/tF8jC/SLpZdTv7+sQIqDq5qkcand5nzkT4
o2Laz6Z5zKj0zwBQ983NQm4Ikz4uJ/eL0vxZ2CtmLi381r1bhWDNyz3KhGbGG7U/
qfE5sTdS3FaFAT8rrsgUb7pwNX2p9jYkn/KKy9Qp1fCFavdSmN/W0JBSH/3zGMCI
346tShf5D7ZF2Wo9Jn1PKP+YLG1zo5f9KUa28sGgCtly1TJlCSsHC7dOVoVg8s6A
mRDlmE+/xL4AgPLVmupUvNn8uW+Y8iNnlZpmtsSvsIv0p2Xj5T/UZPUQZ3FR8YyB
7CiUOTCq/goUIqv3SnpK7prQ31Uxj13UbiAIZGOVjkkSRlC/eX4oYuxCM+iMFzd4
XkH6hVOh67AFU/wri1vzQfCfL78XomlMc+nYviI5dwbO/5uSv5bqEThkFO/9arAt
SVshbq1c5hhFRHE4BuIRTobaJLMJcrWwSyfodAxYWpLpY4BVnO/b8Uc+fRVEnvmG
-----END RSA PRIVATE KEY-----

9
aufs/public.pem

@ -0,0 +1,9 @@
-----BEGIN RSA PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAvWKj7FVU7KtUUWWeV53K
a1t538iiydZI2VEkRSsXt3LIi6j94PsdOmS8e6ZyByGj0T+deVVEdqO7pvJTlZCt
R5z2gut3Z/heG1SF0o5i6YfZvfeyeNWrW6TVQCy+FCweVA6Zlh1QiLubAfYFclTm
5wdF+suZHSnXS1reborotzRGZzq86XEluIQufdvfk55ixY8R/bdoaR4G/SvNzU+2
uBYjOxlBbgtnHmPxs3bAk2wCnPunfjlWw0ngk8UVw7XrAdrIwMItpj4Y0iKUsr5Z
CVpheVmHNfjH8oyio/aERdSXnsM4GksFgOlA9wKQ6opwfQSMhH0mMNThpqzKx/Xt
0wIDAQAB
-----END RSA PUBLIC KEY-----

7
scagent/core/FileRpc.go

@ -61,8 +61,10 @@ func (f *FileService) Compress(args *ZipBlock, reply *string) error {
// 日志输出
logger.Info("压缩文件", zap.String("filename", zpFileName), zap.String("path", realFilePath))
}()
// 年月格式的目录
dirpath := fmt.Sprintf("%d/%02d", time.Now().Year(), time.Now().Month())
// ZIP 文件的实际路径
ziprl := path.Join(config.G.FilePath, "/sync_zips", zpFileName)
ziprl := path.Join(config.G.FilePath, "/sync_zips", dirpath, zpFileName)
// zip 创建成功后
rest := <-taskId
// 如果压缩包生成成功以后再执行
@ -85,6 +87,9 @@ func (f *FileService) Compress(args *ZipBlock, reply *string) error {
// 客户端调用RPC传送文件的函数
// 传入zip的实际路径,远程的服务器带端口,远程文件存放路径
// remote 远程主机地址,带端口
// filePath 本地文件路径
// uploadPath 远程主机的存储路径
func clientUploadFile(remote string, filePath string, uploadPath string) {
fmt.Printf("remote: %s, filePath: %s, uploadPath: %s\n", remote, filePath, uploadPath)
// 启用日志

29
scagent/core/UpFile.go

@ -37,21 +37,16 @@ func (f *UpFileService) SendFileInfo(info FileInfo, reply *string) error {
// }
// 提取路径 /www/wwwroot/bidemo.com/BIU_20250918_183144.zip
// fmt.Printf("SendFileInfo fileName: %s\n", info.FileName)
dirPath := filepath.Dir(info.FileName)
logger.Info("SendFileInfo", zap.String("dirPath", dirPath))
// *reply = fmt.Sprintf("folder: %s is exists", dirPath)
// 检查文件夹是否存在
if _, err := os.Stat(dirPath); os.IsNotExist(err) {
// 文件夹不存在,创建它
if err := os.MkdirAll(dirPath, 0755); err != nil {
logger.Error("SendFileInfo mkdir failed", zap.Error(err))
// *reply = fmt.Sprintf("mkdir folder: %s failed", dirPath)
// return err
}
}
// *reply = fmt.Sprintf("folder: %s is exists", dirPath)
// 返回true的字符串
*reply = "true"
return nil
@ -88,3 +83,27 @@ func (f *UpFileService) SendFileChunk(chunk FileChunk, reply *bool) error {
*reply = true
return nil
}
// 解压缩文件
// info.FileName 压缩包全路径
func (f *UpFileService) UnzipArchive(info FileInfo, reply *string) error {
// 日志
logger := util.NewProductionLogger()
defer logger.Sync()
logger.Info("UnzipArchive", zap.String("filename", info.FileName))
// 解压到的目录
dirpath := filepath.Dir(info.FileName)
// 解压缩文件
ret := make(chan string)
go func() {
util.DecompressZip(info.FileName, dirpath)
ret <- "uzok"
}()
// 返回true的字符串
if retMsg := <-ret; retMsg == "uzok" {
// 删除zip文件
util.DeleteFile(info.FileName)
*reply = retMsg
}
return nil
}

163
scagent/run.sh

@ -0,0 +1,163 @@
#!/bin/bash
# scagent 服务管理脚本
# 用于启动、停止、重启和检查 scagent 服务状态
# 服务名称
APP_NAME="scagent"
# 可执行文件路径
APP_PATH="./scagent"
# PID文件路径
PID_FILE="./${APP_NAME}.pid"
# 日志文件路径
LOG_FILE="./${APP_NAME}.log"
# 检查程序是否存在
check_app_exists() {
if [ ! -f "$APP_PATH" ]; then
echo "错误: 程序 $APP_PATH 不存在,请先编译!"
exit 1
fi
}
# 检查程序是否在运行
is_running() {
if [ -f "$PID_FILE" ]; then
PID=$(cat $PID_FILE)
if ps -p $PID > /dev/null 2>&1; then
return 0 # 运行中
else
# PID文件存在但进程不存在,删除PID文件
rm -f $PID_FILE
fi
fi
return 1 # 未运行
}
# 启动服务
start() {
check_app_exists
if is_running; then
echo "$APP_NAME 服务已经在运行中!"
exit 0
fi
echo "正在启动 $APP_NAME 服务..."
# 以守护进程方式启动应用
nohup $APP_PATH -d > $LOG_FILE 2>&1 &
# 记录PID
echo $! > $PID_FILE
sleep 2
if is_running; then
echo "$APP_NAME 服务启动成功!"
echo "PID: $(cat $PID_FILE)"
else
echo "$APP_NAME 服务启动失败!"
exit 1
fi
}
# 停止服务
stop() {
if ! is_running; then
echo "$APP_NAME 服务未运行!"
exit 0
fi
echo "正在停止 $APP_NAME 服务..."
PID=$(cat $PID_FILE)
kill -15 $PID # 发送 SIGTERM 信号
# 等待进程终止
wait_time=0
max_wait=10
while is_running && [ $wait_time -lt $max_wait ]; do
sleep 1
wait_time=$((wait_time + 1))
echo -n "."
done
echo
if is_running; then
echo "强制终止 $APP_NAME 服务..."
kill -9 $PID
rm -f $PID_FILE
else
rm -f $PID_FILE
echo "$APP_NAME 服务已停止!"
fi
}
# 重启服务
restart() {
echo "正在重启 $APP_NAME 服务..."
stop
sleep 2
start
}
# 查看状态
status() {
if is_running; then
echo "$APP_NAME 服务正在运行中"
echo "PID: $(cat $PID_FILE)"
else
echo "$APP_NAME 服务未运行"
fi
}
# 查看日志
tail_log() {
if [ -f "$LOG_FILE" ]; then
tail -f $LOG_FILE
else
echo "日志文件不存在: $LOG_FILE"
fi
}
# 显示帮助信息
usage() {
echo "使用方法: $0 {start|stop|restart|status|log}"
echo " start 启动服务"
echo " stop 停止服务"
echo " restart 重启服务"
echo " status 查看服务状态"
echo " log 查看日志输出"
}
# 检查参数
if [ $# -eq 0 ]; then
usage
exit 1
fi
# 根据参数执行相应的命令
case "$1" in
start)
start
;;
stop)
stop
;;
restart)
restart
;;
status)
status
;;
log)
tail_log
;;
*)
usage
exit 1
;;
esac
exit 0

60
scagent/util/fsutil.go

@ -14,6 +14,7 @@ import (
"path/filepath"
"scagent/config"
"strings"
"time"
"github.com/schollz/progressbar/v3"
"go.uber.org/zap"
@ -266,8 +267,9 @@ func ZipFiles(zipname string, files []string, zippath string) error {
// 解压缩zip文件
// 这个方法必须是首字母大写的,才能被注册
// src: 需要解压的zip文件
func DecompressZip(zpFname string) error {
// zpFname: 需要解压的zip文件
// dirpath: 解压到的目录
func DecompressZip(zpFname string, dirpath string) error {
// 下载文件
archive, err := zip.OpenReader(zpFname)
if err != nil {
@ -279,7 +281,7 @@ func DecompressZip(zpFname string) error {
// 遍历目录
for _, f := range archive.File {
// zip 解压的时候,路径为监听的路径
filePath := filepath.Join(config.G.FilePath, f.Name)
filePath := filepath.Join(dirpath, f.Name)
// 暂停原因 屏蔽解压到files 目录下
//filePath := filepath.Join(dir, f.Name)
if f.FileInfo().IsDir() {
@ -295,15 +297,22 @@ func DecompressZip(zpFname string) error {
if err != nil {
return fmt.Errorf("failed to create file (%v)", err)
}
defer dstFile.Close()
// 打开zip文件中的文件
fileInArchive, err := f.Open()
if err != nil {
return fmt.Errorf("failed to open file in zip (%v)", err)
}
defer fileInArchive.Close()
// 复制文件到目标路径
if _, err := io.Copy(dstFile, fileInArchive); err != nil {
return fmt.Errorf("failed to copy file in zip (%v)", err)
}
dstFile.Close()
fileInArchive.Close()
// 保留原始文件的修改时间
if err := os.Chtimes(filePath, f.ModTime(), f.ModTime()); err != nil {
return fmt.Errorf("failed to set file times (%v)", err)
}
// nothing
}
return nil
}
@ -311,14 +320,17 @@ func DecompressZip(zpFname string) error {
// dest:目的地址以及压缩文件名 eg:/data/logZip/2022-12-12-log.zip
// paths:需要压缩的所有文件所组成的集合
func CompressToZip(dest string, currentPath string, paths []string) error {
// fmt.Printf("currentPath:%s\n", currentPath)
// fmt.Printf("paths len:%d\n", len(paths))
// fmt.Println("real path", currentPath)
// 打开files 目录
filesPath := ""
if dir, err := os.Getwd(); err == nil {
filesPath = dir + "/sync_zips/"
}
// 目录格式 2025/10
dirpath := fmt.Sprintf("%d/%02d", time.Now().Year(), time.Now().Month())
// 组织目录路径
filesPath = filepath.Join(filesPath, dirpath)
// 创建目录
if err := os.MkdirAll(filesPath, os.ModePerm); err != nil {
fmt.Println(err.Error())
}
@ -384,6 +396,10 @@ func CompressToZip(dest string, currentPath string, paths []string) error {
return err
}
defer f.Close()
// 保留文件的原有的时间
header.Modified = info.ModTime()
// 保留文件的原有的权限
header.SetMode(info.Mode())
_, err = io.Copy(headerWriter, f)
return err
})
@ -393,7 +409,8 @@ func CompressToZip(dest string, currentPath string, paths []string) error {
}
}
// 存到指定位置
os.Rename(dest, path.Join("./sync_zips/", dest))
// os.Rename(dest, path.Join("./sync_zips/", dest))
os.Rename(path.Join("./sync_zips/", dest), path.Join(filesPath, dest))
return nil
}
@ -415,3 +432,28 @@ func ReadConfig() {
}
config.G.Port = fmt.Sprintf("%d", iport)
}
// 删除文件
func DeleteFile(filePath string) error {
return os.Remove(filePath)
}
// 删除过期文件
func DeleteExpiredFiles(dirPath string, expiredTime time.Duration) error {
// 遍历目录下的所有文件
err := filepath.Walk(dirPath, func(path string, info os.FileInfo, err error) error {
if err != nil {
return err
}
// 检查文件是否过期
if info.IsDir() {
return nil
}
if time.Since(info.ModTime()) > expiredTime {
// 删除过期文件
return DeleteFile(path)
}
return nil
})
return err
}

2
scalib/build.bat

@ -1,5 +1,5 @@
@echo off
color 87
color 8f
set GOOS=linux
set GOARCH=amd64
set CGO_ENABLED=0

16
scalib/main.go

@ -36,7 +36,7 @@ type UpFileClient struct {
}
// todo 后续增加安全验证
// 使用方法 eg: ./scalib -h 192.168.66.92:9098 -f /www/gfs/sync_zips/BIU_20251009_153640.zip /www/afs/logs/
// 使用方法 eg: ./scalib -h 192.168.66.92:9098 -f /www/gfs/sync_zips/BIU_20251009_153640.zip -u /www/afs/logs/
// 主入口
func main() {
// 启用日志
@ -157,8 +157,20 @@ func transferFile(c *UpFileClient, curPath string, uploadPath string) error {
progress := float64(offset) / float64(fileInfo.Size()) * 100
fmt.Printf("\r发送进度: %.2f%%", progress)
}
fmt.Println("\n文件发送完成!")
// 文件解压缩
distFullName := filepath.Join(dirPath, fileName)
fmt.Printf("distFullName: %s\n", distFullName)
// 此时调用远程的完整路径,防止解压缩不成功
c.rpcClient.Call("UpFileService.UnzipArchive", FileInfo{
FileName: distFullName,
FileSize: fileInfo.Size(),
}, &reply)
// 输出执行的结果
if reply != "true" {
logger.Error("UnzipArchive failed", zap.String("reply", reply))
}
fmt.Printf("UnzipArchive success, reply: %s\n", reply)
// }()

14
scalib/readme.md

@ -0,0 +1,14 @@
## 说明
作为文件调用远程rpc接口的工具,用来传输指定的文件到远程服务器,文件需要和scagent 配合使用,仅作为scanget的组件。
文件编译后的二进制可执行文件名为scalib,基本使用的方法
```
./scalib -h 192.168.66.92:9098 -f /www/gfs/sync_zips/BIU_20251009_153640.zip -u /www/afs/logs/
-h 目标主机
-f 待传输的文件
-u 传输到目标服务器的存储路径
```

192
scalib/util/fsutil.go

@ -0,0 +1,192 @@
package util
import (
"archive/zip"
"crypto/sha256"
"encoding/base64"
"fmt"
"io"
"os"
"path"
"path/filepath"
"strings"
)
// base64 encode
// url safe
func Bas64end(str string) string {
// bdata:=
return base64.URLEncoding.EncodeToString([]byte(str))
}
// base64 url safe uneconde
func Base64dec(bsstr string) string {
dedc, _ := base64.URLEncoding.DecodeString(bsstr)
return string(dedc)
}
// 计算文件的hash
func CalacHash(rfile string) string {
// 获取到真实地址
//
file, err := os.Open(rfile)
if err != nil {
panic(err)
}
defer file.Close()
// initlize hash object
hash := sha256.New()
// write file into hash object
if _, err := io.Copy(hash, file); err != nil {
panic(err)
}
// get hash value
hashBytes := hash.Sum(nil)
//converto to hash string
hastString := fmt.Sprintf("%x", hashBytes)
return hastString
}
// 判断文件是否存在
func IsFileExist(filename string) bool {
if _, err := os.Stat(filename); err == nil {
return true
}
return false
}
// 解压缩zip文件
// 这个方法必须是首字母大写的,才能被注册
// src: 需要解压的zip文件
// curPath: 解压到的路径
func DecompressZip(zpFname string, curPath string) error {
// 下载文件
archive, err := zip.OpenReader(zpFname)
if err != nil {
return err
}
// 取zip文件的绝对路径的文件夹。会默认解压到files下,暂停使用
// dir := filepath.Dir(zpFname)
defer archive.Close()
// 遍历目录
for _, f := range archive.File {
// zip 解压的时候,路径为监听的路径
filePath := filepath.Join(curPath, f.Name)
// 暂停原因 屏蔽解压到files 目录下
//filePath := filepath.Join(dir, f.Name)
if f.FileInfo().IsDir() {
os.MkdirAll(filePath, os.ModePerm)
continue
}
// 父文件夹开始创建目录
if err := os.MkdirAll(filepath.Dir(filePath), os.ModePerm); err != nil {
return fmt.Errorf("failed to make directory (%v)", err)
}
// 文件原有模式
dstFile, err := os.OpenFile(filePath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, f.Mode())
if err != nil {
return fmt.Errorf("failed to create file (%v)", err)
}
fileInArchive, err := f.Open()
if err != nil {
return fmt.Errorf("failed to open file in zip (%v)", err)
}
if _, err := io.Copy(dstFile, fileInArchive); err != nil {
return fmt.Errorf("failed to copy file in zip (%v)", err)
}
dstFile.Close()
fileInArchive.Close()
}
return nil
}
// dest:目的地址以及压缩文件名 eg:/data/logZip/2022-12-12-log.zip
// paths:需要压缩的所有文件所组成的集合
func CompressToZip(dest string, currentPath string, paths []string) error {
// fmt.Printf("currentPath:%s\n", currentPath)
// fmt.Printf("paths len:%d\n", len(paths))
// fmt.Println("real path", currentPath)
// 打开files 目录
filesPath := ""
if dir, err := os.Getwd(); err == nil {
filesPath = dir + "/sync_zips/"
}
if err := os.MkdirAll(filesPath, os.ModePerm); err != nil {
fmt.Println(err.Error())
}
// ToSlash 过滤windows的斜杠引起的bug
zfile, err := os.Create(path.Join("./sync_zips", "/", dest))
if err != nil {
return err
}
defer zfile.Close()
zipWriter := zip.NewWriter(zfile)
defer zipWriter.Close()
// 遍历带压缩的目录信息
for _, src := range paths {
// 替换文件名,并且去除前后 "\" 或 "/"
// path = strings.Trim(path, string(filepath.Separator))
// 从配置中读取到源目录
src = path.Join(currentPath, "/", src)
// 删除尾随路径(如果它是目录)
src = strings.TrimSuffix(src, string(os.PathSeparator))
// fmt.Printf("zip src:%s\n", src)
// 文件遍历
err = filepath.Walk(src, func(path string, info os.FileInfo, err error) error {
// fmt.Printf("zip path:%s\n", path)
if err != nil {
return err
}
// 创建本地文件头
header, err := zip.FileInfoHeader(info)
if err != nil {
return err
}
// 监听的工作目录作为文件的结尾,然后取相对路径
// 在zip存档中设置文件的相对路径
// 将压缩包中的绝对路径替换为相对路径
header.Name, err = filepath.Rel(filepath.Dir(src), path)
if err != nil {
return err
}
// 转换为Unix风格的路径分隔符
header.Name = filepath.ToSlash(header.Name)
// 目录需要拼上一个 "/" ,否则会出现一个和目录一样的文件在压缩包中
if info.IsDir() {
header.Name += "/"
} else {
// 将压缩方法设置为deflate
header.Method = zip.Deflate
}
// fmt.Printf("zip header:%v\n", header.Name)
// 创建写入头的writer
headerWriter, err := zipWriter.CreateHeader(header)
if err != nil {
return err
}
if info.IsDir() {
return nil
}
f, err := os.Open(path)
if err != nil {
return err
}
defer f.Close()
_, err = io.Copy(headerWriter, f)
return err
})
if err != nil {
return err
}
}
// 存到指定位置
os.Rename(dest, path.Join("./sync_zips/", dest))
return nil
}

30
vue/afvue/package-lock.json

@ -7,13 +7,17 @@
"": {
"name": "afvue",
"version": "0.0.0",
"license": "MIT",
"dependencies": {
"axios": "^1.11.0",
"event-source-polyfill": "^1.0.31",
"lodash": "^4.17.21",
"nprogress": "^0.2.0",
"pinia": "^2.0.16",
"store": "^2.0.12",
"vue": "^2.7.7",
"vue-router": "^3.5.4"
"vue-router": "^3.5.4",
"vuex": "^3.1.1"
},
"devDependencies": {
"@vitejs/plugin-legacy": "^2.0.0",
@ -706,6 +710,12 @@
"node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
}
},
"node_modules/nprogress": {
"version": "0.2.0",
"resolved": "https://registry.npmmirror.com/nprogress/-/nprogress-0.2.0.tgz",
"integrity": "sha512-I19aIingLgR1fmhftnbWWO3dXc0hSxqHQHQb3H8m+K3TnEn/iSeTZZOyvKXWqQESMwuUVnatlCnZdLBZZt2VSA==",
"license": "MIT"
},
"node_modules/path-parse": {
"version": "1.0.7",
"resolved": "https://registry.npmmirror.com/path-parse/-/path-parse-1.0.7.tgz",
@ -896,6 +906,15 @@
"dev": true,
"license": "MIT"
},
"node_modules/store": {
"version": "2.0.12",
"resolved": "https://registry.npmmirror.com/store/-/store-2.0.12.tgz",
"integrity": "sha512-eO9xlzDpXLiMr9W1nQ3Nfp9EzZieIQc10zPPMP5jsVV7bLOziSFFBP0XoDXACEIFtdI+rIz0NwWVA/QVJ8zJtw==",
"license": "MIT",
"engines": {
"node": "*"
}
},
"node_modules/supports-preserve-symlinks-flag": {
"version": "1.0.0",
"resolved": "https://registry.npmmirror.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz",
@ -1033,6 +1052,15 @@
"resolved": "https://registry.npmmirror.com/vue-router/-/vue-router-3.6.5.tgz",
"integrity": "sha512-VYXZQLtjuvKxxcshuRAwjHnciqZVoXAjTjcqBTz4rKc8qih9g9pI3hbDjmqXaHdgL3v8pV6P8Z335XvHzESxLQ==",
"license": "MIT"
},
"node_modules/vuex": {
"version": "3.6.2",
"resolved": "https://registry.npmmirror.com/vuex/-/vuex-3.6.2.tgz",
"integrity": "sha512-ETW44IqCgBpVomy520DT5jf8n0zoCac+sxWnn+hMe/CzaSejb/eVw2YToiXYX+Ex/AuHHia28vWTq4goAexFbw==",
"license": "MIT",
"peerDependencies": {
"vue": "^2.0.0"
}
}
}
}

5
vue/afvue/package.json

@ -12,9 +12,12 @@
"axios": "^1.11.0",
"event-source-polyfill": "^1.0.31",
"lodash": "^4.17.21",
"nprogress": "^0.2.0",
"pinia": "^2.0.16",
"store": "^2.0.12",
"vue": "^2.7.7",
"vue-router": "^3.5.4"
"vue-router": "^3.5.4",
"vuex": "^3.1.1"
},
"devDependencies": {
"@vitejs/plugin-legacy": "^2.0.0",

9
vue/afvue/src/App.vue

@ -1,7 +1,8 @@
<template>
<div id="app">
<div class="hdbox">
<!-- 登录成功后显示 -->
<div class="hdbox" v-if="isAuthenticated">
<div class="logobox">
<img alt="afvue" class="logo" src="@/assets/logo.svg" width="80" />
</div>
@ -12,10 +13,12 @@
<router-link to="/setting">系统设置</router-link>
</nav>
</div>
</div>
<!--详细内容 -->
<!-- 未登录时显示 -->
<div v-else>
<router-view />
</div>
</div>
</template>

9
vue/afvue/src/api/scinfo.js

@ -60,3 +60,12 @@ export function SendZipFile(data){
data
})
}
// 用户登录
export function login(data){
return axios({
url: '/login',
method: 'POST',
data
})
}

38
vue/afvue/src/assets/main.css

@ -87,4 +87,42 @@ a,
}
.tb-php{
background-position: 0px -852px;
}
/*
对话框
*/
.dialog{
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, 0.5);
float: left;
left: 0;
top: 0;
position: fixed;
animation: fadeOut 3s ease-in-out forwards;
}
.dialog .alert{
position: fixed;
top: 50%;
left: 50%;
transform: translateX(-50%);
padding: 10px 20px;
border-radius: 5px;
z-index: 9999;
/* 3s后消失 */
animation: fadeOut 3s ease-in-out forwards;
}
/** 同步成功提示 3s后消失*/
.alert-success{
/* display: none; */
background-color: #aee9bc;
color: #fff;
}
@keyframes fadeOut {
0% {
opacity: 1;
}
100% {
opacity: 0;
}
}

213
vue/afvue/src/assets/sfilecompare.css

@ -0,0 +1,213 @@
.sfcbox {
float: left;
width: 100%;
/* overflow-y: auto; */
margin: 20px auto;
margin-top: 12px;
background-color: #fff;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
border-radius: 12px 12px 0 0;
position: relative;
}
.sfcon {
padding: 20px;
float: left;
width: 100%;
min-height: 560px;
margin-bottom: 40px;
}
.sfcon:first-child {
margin-bottom: 22px;
box-shadow: 1px 7px 10px 3px rgba(0, 0, 0, 0.1);
}
.sfcon:nth-child(2) {
margin-top: 22px;
}
.sfcon:nth-child(2) .sflist {
background-color: #dddcdc5e;
}
.sfbcon .sflist{
position: relative;
}
/* 操作按钮 */
/* .sfcon .sflist .fbdiv{
position: absolute;
bottom: 0;
left: 0;
height: 46px;
background-color: #00bd7e;
width: 100%;
} */
/* .sfcon .sflist .fbdiv .fbtn{
margin: 12px;
} */
.sfind {
width: 100%;
/* height: 40px; */
line-height: 40px;
padding: 12px;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
}
.sfind .selbox {
height: 40px;
border-top: none;
border-left: none;
border-right: none;
outline: none;
padding: 0 12px;
}
.sfind .inputbox {
height: 40px;
line-height: 40px;
padding: 12px;
border-top: none;
border-left: none;
border-right: none;
margin-left: 12px;
outline: none;
width: 460px;
}
.sfind button {
width: 70px;
height: 40px;
margin-left: 12px;
background-color: #f5f5f5;
border: none;
}
.sfcon .sflist {
min-height: 460px;
overflow-y: auto;
background-color: #f5f5f5;
}
.sfcon .sflist ul {
margin-top: 20px;
display: block;
width: 100%;
min-height: 500px;
}
.sfcon .sflist ul li {
list-style: none;
width: 100%;
float: left;
height: 40px;
line-height: 40px;
border-bottom: 1px dashed #ccc;
}
.sfcon .sflist ul li:hover {
background-color: #dad8d8;
}
.sfcon .sflist ul li .sflfunc {
/* display: none; */
float: right;
width: 360px;
margin-right: 20px;
}
.sfcon .sflist ul li .sfchkbox {
margin-right: 12px;
width: 18px;
height: 18px;
}
.sfcon .sflist ul li span{
line-height: 30px;
}
.sfcon .sflist .haschild {
cursor: pointer;
}
.sfcon .sflist .haschild i{
display: inline-block;
width: 25px;
height: 25px;
background-image: url("@/assets/css_sprites.png");
background-repeat: no-repeat;
background-size: 25px;
background-position: 0px -400px;
vertical-align: middle;
}
.sfcon .sflist .tips {
width: 100%;
float: left;
margin-top: 12px;
}
/* 传输功能区 */
.sftrans {
position: fixed;
width: 200px;
height: 120px;
z-index: 99;
background-color: #f5f5f5;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
}
.sftrans .sfbcon {
color: #333;
margin-left: auto;
margin-right: auto;
background-color: #fff;
padding: 10px;
}
.sftrans .sfbcon li {
list-style: none;
height: 30px;
line-height: 30px;
}
.sftrans .sfbcon li:hover {
background-color: #00bd7e;
color: #fff;
cursor: default;
}
.sftrans .sfbcon li span {
padding-left: 12px;
}
.sftrans .sfbcon li.divider{
height: 1px;
background-color: #00bd7e;
margin: 10px 0;
}
/** 发送文件 */
.fsend{
float: left;
height: 60px;
background-color: #f3efef;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
margin-top: 20px;
width: 100%;
position:fixed;
bottom: 0;
left: 0;
}
.fsend .fsendcon{
margin-left: auto;
padding: 12px;
background-color: #fff;
min-height: 60px;
width: 1200px;
margin-left: auto;
margin-right: auto;
}
.diff{
background-color: #fcc;
}

28
vue/afvue/src/components/LoginLayout.vue

@ -0,0 +1,28 @@
<template>
<div class="login-layout">
<div class="login-content">
<slot></slot>
</div>
</div>
</template>
<script>
export default {
name: 'LoginLayout'
}
</script>
<style scoped>
.login-layout {
display: flex;
justify-content: center;
align-items: center;
min-height: 100vh;
background-color: #f5f5f5;
}
.login-content {
width: 100%;
max-width: 400px;
}
</style>

0
vue/afvue/src/components/index.less

76
vue/afvue/src/components/nprogress.css

@ -0,0 +1,76 @@
/* Make clicks pass-through */
#nprogress {
pointer-events: none;
}
#nprogress .bar {
/* background: @primary-color; */
background-color: rgb(224, 89, 82);
position: fixed;
z-index: 1031;
top: 0;
left: 0;
width: 100%;
height: 2px;
}
/* Fancy blur effect */
#nprogress .peg {
display: block;
position: absolute;
right: 0px;
width: 100px;
height: 100%;
/* box-shadow: 0 0 10px @primary-color, 0 0 5px @primary-color; */
box-shadow: 0 0 10px rgb(224, 89, 82), 0 0 5px rgb(220, 215, 215);
opacity: 1.0;
-webkit-transform: rotate(3deg) translate(0px, -4px);
-ms-transform: rotate(3deg) translate(0px, -4px);
transform: rotate(3deg) translate(0px, -4px);
}
/* Remove these to get rid of the spinner */
#nprogress .spinner {
display: block;
position: fixed;
z-index: 1031;
top: 15px;
right: 15px;
}
#nprogress .spinner-icon {
width: 18px;
height: 18px;
box-sizing: border-box;
border: solid 2px transparent;
border-top-color: rgb(224, 89, 82);
border-left-color: rgb(224, 89, 82);
border-radius: 50%;
-webkit-animation: nprogress-spinner 400ms linear infinite;
animation: nprogress-spinner 400ms linear infinite;
}
.nprogress-custom-parent {
overflow: hidden;
position: relative;
}
.nprogress-custom-parent #nprogress .spinner,
.nprogress-custom-parent #nprogress .bar {
position: absolute;
}
@-webkit-keyframes nprogress-spinner {
0% { -webkit-transform: rotate(0deg); }
100% { -webkit-transform: rotate(360deg); }
}
@keyframes nprogress-spinner {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}

7
vue/afvue/src/main.js

@ -3,14 +3,19 @@ import { createPinia, PiniaVuePlugin } from 'pinia'
import { VueAxios } from './utils/request'
import App from './App.vue'
import router from './router'
import store from './store' // 导入Vuex store
// 导入权限控制模块
import './permission'
import './assets/main.css'
Vue.use(PiniaVuePlugin)
const pinia = createPinia()
Vue.use(VueAxios)
new Vue({
router,
pinia: createPinia(),
store, // 使用Vuex store
render: (h) => h(App)
}).$mount('#app')

46
vue/afvue/src/permission.js

@ -0,0 +1,46 @@
import router from './router'
import store from './store' // 导入Vuex store
import NProgress from 'nprogress' // progress bar
import '@/components/nprogress.css' // progress bar style
NProgress.configure({ showSpinner: false }) // NProgress Configuration
// 登录页面
const loginRoutePath = '/login'
// 白名单
const whiteList = [loginRoutePath, '/404', '/401']
// 默认路由地址
const defaultRoutePath = '/'
router.beforeEach((to, from, next) => {
NProgress.start() // start progress bar
// 检查是否在白名单中
if (whiteList.includes(to.path)) {
next()
NProgress.done()
return
}
// 使用Vuex store检查登录状态
let token = ''
try {
token = store.state.user.token || localStorage.getItem('token')
} catch (e) {
console.error('Error reading token:', e)
}
// 检查登录状态
if (token) {
next()
} else {
// 未登录,重定向到登录页
next({ path: loginRoutePath })
NProgress.done()
}
})
// 路由守卫
router.afterEach((to, from) => {
NProgress.done() // finish progress bar
})

7
vue/afvue/src/router/index.js

@ -55,6 +55,11 @@ const router = new VueRouter({
// this generates a separate chunk (About.[hash].js) for this route
// which is lazy-loaded when the route is visited.
component: () => import('../views/AboutView.vue')
},
{
path: '/login',
name: 'login',
component: () => import('../views/Login.vue')
}
]
})
@ -68,4 +73,4 @@ const router = new VueRouter({
// }
// })
export default router
export default router

35
vue/afvue/src/store/index.js

@ -0,0 +1,35 @@
import Vue from 'vue'
import Vuex from 'vuex'
import user from './modules/user'
Vue.use(Vuex)
const store = new Vuex.Store({
modules: {
user
},
state: {
// 全局状态
},
mutations: {
// 全局mutations
},
actions: {
// 全局actions
},
getters: {
// 全局getters
}
})
// 初始化时从localStorage恢复用户状态
try {
const token = localStorage.getItem('token')
if (token) {
store.commit('user/SET_TOKEN', token)
}
} catch (e) {
console.error('Error reading token from localStorage:', e)
}
export default store

111
vue/afvue/src/store/modules/user.js

@ -0,0 +1,111 @@
import storage from 'store'
import { login } from '@/api/scinfo'
const user = {
namespaced: true,
state: {
token: '',
userInfo: null,
isLoggedIn: false,
permissions: []
},
mutations: {
// 设置token
SET_TOKEN(state, token) {
state.token = token
state.isLoggedIn = !!token
},
// 设置用户信息
SET_USER_INFO(state, userInfo) {
state.userInfo = userInfo
},
// 设置权限列表
SET_PERMISSIONS(state, permissions) {
state.permissions = permissions
},
// 登出
LOGOUT(state) {
state.token = ''
state.userInfo = null
state.isLoggedIn = false
state.permissions = []
storage.remove('token')
}
},
actions: {
// 用户登录
async login({ commit }, userData) {
try {
const res = await login(userData)
if (res && res.data && res.data.token) {
// 保存token到localStorage
storage.set('token', res.data.token)
// 更新store状态
commit('SET_TOKEN', res.data.token)
// 如果有用户信息,同时设置
if (res.data.userInfo) {
commit('SET_USER_INFO', res.data.userInfo)
}
// 如果有权限信息,设置权限
if (res.data.permissions) {
commit('SET_PERMISSIONS', res.data.permissions)
}
return true
}
return false
} catch (error) {
console.error('登录失败:', error)
throw error
}
},
// 获取用户信息
async getUserInfo({ commit, state }) {
if (!state.token) return
try {
// 这里可以调用获取用户信息的API
// 暂时模拟数据
const userInfo = {
username: storage.get('username') || '未知用户',
// 其他用户信息
}
commit('SET_USER_INFO', userInfo)
return userInfo
} catch (error) {
console.error('获取用户信息失败:', error)
// 信息获取失败,清除登录状态
commit('LOGOUT')
throw error
}
},
// 用户登出
logout({ commit }) {
commit('LOGOUT')
}
},
getters: {
// 获取token
getToken: state => state.token,
// 获取用户信息
getUserInfo: state => state.userInfo,
// 判断是否登录
isLoggedIn: state => state.isLoggedIn,
// 获取权限列表
getPermissions: state => state.permissions,
// 判断是否有权限
hasPermission: state => permission => {
return state.permissions.includes(permission)
}
}
}
export default user

16
vue/afvue/src/stores/counter.js

@ -1,16 +0,0 @@
import { defineStore } from 'pinia'
export const useCounterStore = defineStore({
id: 'counter',
state: () => ({
counter: 0
}),
getters: {
doubleCount: (state) => state.counter * 2
},
actions: {
increment() {
this.counter++
}
}
})

145
vue/afvue/src/views/Login.vue

@ -0,0 +1,145 @@
<template>
<LoginLayout>
<div class="login-form">
<h2>系统登录</h2>
<div class="form-group">
<label for="username">用户名</label>
<input type="text" id="username" v-model="username" placeholder="请输入用户名" />
</div>
<div class="form-group">
<label for="password">密码</label>
<input type="password" id="password" v-model="password" placeholder="请输入密码" />
</div>
<button class="login-button" @click="handleLogin" :disabled="loading">
{{ loading ? '登录中...' : '登录' }}
</button>
<div v-if="error" class="error-message">{{ error }}</div>
</div>
</LoginLayout>
</template>
<script>
import { mapActions } from 'vuex'
import router from '@/router'
import storage from 'store'
import LoginLayout from '@/components/LoginLayout.vue'
export default {
components: {
LoginLayout
},
name: 'Login',
data() {
return {
username: '',
password: '',
loading: false,
error: ''
}
},
methods: {
...mapActions({
loginAction: 'user/login'
}),
async handleLogin() {
if (!this.username || !this.password) {
this.error = '请输入用户名和密码'
return
}
this.loading = true
this.error = ''
try {
// Vuex storeaction
const success = await this.loginAction({
username: this.username,
password: this.password
})
if (success) {
// localStorage便使
storage.set('username', this.username)
//
router.push('/')
} else {
this.error = '登录失败,请检查用户名和密码'
}
} catch (err) {
console.error('Login error:', err)
this.error = '登录失败,请稍后重试'
} finally {
this.loading = false
}
}
}
}
</script>
<style scoped>
.login-form {
background: white;
padding: 40px;
border-radius: 8px;
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.1);
width: 350px;
}
.login-form h2 {
text-align: center;
margin-bottom: 30px;
color: #333;
}
.form-group {
margin-bottom: 20px;
}
.form-group label {
display: block;
margin-bottom: 8px;
color: #666;
font-size: 14px;
}
.form-group input {
width: 100%;
padding: 10px;
border: 1px solid #ddd;
border-radius: 4px;
font-size: 14px;
}
.form-group input:focus {
outline: none;
border-color: #42b983;
}
.login-button {
width: 100%;
padding: 12px;
background-color: #42b983;
color: white;
border: none;
border-radius: 4px;
font-size: 16px;
cursor: pointer;
margin-bottom: 15px;
}
.login-button:hover:not(:disabled) {
background-color: #359c6d;
}
.login-button:disabled {
background-color: #ccc;
cursor: not-allowed;
}
.error-message {
color: #ff4d4f;
font-size: 14px;
text-align: center;
}
</style>

301
vue/afvue/src/views/Sfilecompare.vue

@ -19,7 +19,7 @@
<div class="sflist">
<form method="post" ref="fsform">
<ul>
<li v-for="(item, index) in fsclist" :key="item" :data-index="index">
<li v-for="(item, index) in fsclist" :key="item" :data-index="index" :id="'fs_'+rc32(item.path)" :class="{'diff': item.path in diffFiles}">
<input type="checkbox" :value="item.path" class="sfchkbox" :name="'fsbox' + index" :ref="'fsbox' + index"/>
<a class="haschild" @click="goIntoDir(item.path, 1)" v-if="item.isdir"><i></i>{{ item.path }}</a>
<span v-else><i :class="'tb tb-'+item.suffix"></i>{{ item.path }}</span>
@ -83,7 +83,7 @@
</div>
<!-- 待发送文件 -->
<div class="fsend">
<div class="fsend" v-if="hasTwolist">
<div class="fsendcon">
<!-- 暂未想好放啥 -->
<button class="fbtn" @click="submitForm">同步选中的文件</button>
@ -91,6 +91,14 @@
</div>
<!-- 弹框提示 -->
<div class="dialog" v-if="dialogShow">
<div class="alert alert-success">
同步成功
</div>
</div>
</div>
</template>
@ -116,9 +124,11 @@ export default {
isMenuVisible: false, //
menuTop: 0, //
menuLeft: 0, //
dialogShow: false, //
isShowDir: false, //
hasTwolist: false, //
diffFiles: [], //
}
},
mounted() {
@ -163,6 +173,63 @@ export default {
GetFileList({ srcip: scip, path: this.sspath, sport: sport }).then(res => {
this.ssclist = res.data.list
})
// 3s
setTimeout(() => {
this.diffFlist()
}, 3000)
//
if (this.fsclist.length > 0 && this.ssclist.length > 0) {
this.hasTwolist=true
}
},
// web
diffFlist(){
let alist = this.fsclist;
let blist = this.ssclist;
// alist blist
let diff = [...alist].filter(item =>
!blist.some(i =>
// i.hash == item.hash
i.path == item.path
)
)
// diff
// diff = diff.flatMap(item => item.hash)
diff = diff.map(item => item.path)
//
this.diffFiles = diff
},
// rc32,
rc32(str) {
// utf-8
str = encodeURIComponent(str)
let rc = 0xffff
for (let i = 0; i < str.length; i++) {
rc = (rc << 5) + rc + str.charCodeAt(i)
// 使
rc = rc & 0x7FFFFFFF
}
return rc
},
// list
compareList(){
let alist = this.fsclist;
let blist = this.ssclist;
// alist blist
let diff = [...alist].filter(item =>
!blist.some(i =>
// i.hash == item.hash
i.path == item.path
)
)
// diff
// diff = diff.flatMap(item => item.hash)
diff = diff.map(item => item.path)
//
this.diffFiles = diff
},
//
goIntoDir(path, nflag) {
@ -336,8 +403,22 @@ export default {
// build to schema
const pdata = new URLSearchParams(data)
//
SendZipFile(pdata).then(res=>{
SendZipFile(pdata).then(res=>{
console.log(res,"res")
// alert(res.status)
if(res.status == "200"){
//
this.dialogShow = true
// 3s
setTimeout(() => {
this.dialogShow = false
}, 3000);
//
this.getSflist()
}
// alert(res.data.reply)
//
this.selectedFiles = []
// pdata
@ -362,213 +443,5 @@ export default {
</script>
<style scoped>
.sfcbox {
float: left;
width: 100%;
/* overflow-y: auto; */
margin: 20px auto;
margin-top: 12px;
background-color: #fff;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
border-radius: 12px 12px 0 0;
position: relative;
}
.sfcon {
padding: 20px;
float: left;
width: 100%;
min-height: 560px;
margin-bottom: 40px;
}
.sfcon:first-child {
margin-bottom: 22px;
box-shadow: 1px 7px 10px 3px rgba(0, 0, 0, 0.1);
}
.sfcon:nth-child(2) {
margin-top: 22px;
}
.sfcon:nth-child(2) .sflist {
background-color: #dddcdc5e;
}
.sfbcon .sflist{
position: relative;
}
/* 操作按钮 */
/* .sfcon .sflist .fbdiv{
position: absolute;
bottom: 0;
left: 0;
height: 46px;
background-color: #00bd7e;
width: 100%;
} */
/* .sfcon .sflist .fbdiv .fbtn{
margin: 12px;
} */
.sfind {
width: 100%;
/* height: 40px; */
line-height: 40px;
padding: 12px;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
}
.sfind .selbox {
height: 40px;
border-top: none;
border-left: none;
border-right: none;
outline: none;
padding: 0 12px;
}
.sfind .inputbox {
height: 40px;
line-height: 40px;
padding: 12px;
border-top: none;
border-left: none;
border-right: none;
margin-left: 12px;
outline: none;
width: 460px;
}
.sfind button {
width: 70px;
height: 40px;
margin-left: 12px;
background-color: #f5f5f5;
border: none;
}
.sfcon .sflist {
min-height: 460px;
overflow-y: auto;
background-color: #f5f5f5;
}
.sfcon .sflist ul {
margin-top: 20px;
display: block;
width: 100%;
min-height: 500px;
}
.sfcon .sflist ul li {
list-style: none;
width: 100%;
float: left;
height: 40px;
line-height: 40px;
border-bottom: 1px dashed #ccc;
}
.sfcon .sflist ul li:hover {
background-color: #dad8d8;
}
.sfcon .sflist ul li .sflfunc {
/* display: none; */
float: right;
width: 360px;
margin-right: 20px;
}
.sfcon .sflist ul li .sfchkbox {
margin-right: 12px;
width: 18px;
height: 18px;
}
.sfcon .sflist ul li span{
line-height: 30px;
}
.sfcon .sflist .haschild {
cursor: pointer;
}
.sfcon .sflist .haschild i{
display: inline-block;
width: 25px;
height: 25px;
background-image: url("@/assets/css_sprites.png");
background-repeat: no-repeat;
background-size: 25px;
background-position: 0px -400px;
vertical-align: middle;
}
.sfcon .sflist .tips {
width: 100%;
float: left;
margin-top: 12px;
}
/* 传输功能区 */
.sftrans {
position: fixed;
width: 200px;
height: 120px;
z-index: 99;
background-color: #f5f5f5;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
}
.sftrans .sfbcon {
color: #333;
margin-left: auto;
margin-right: auto;
background-color: #fff;
padding: 10px;
}
.sftrans .sfbcon li {
list-style: none;
height: 30px;
line-height: 30px;
}
.sftrans .sfbcon li:hover {
background-color: #00bd7e;
color: #fff;
cursor: default;
}
.sftrans .sfbcon li span {
padding-left: 12px;
}
.sftrans .sfbcon li.divider{
height: 1px;
background-color: #00bd7e;
margin: 10px 0;
}
/** 发送文件 */
.fsend{
float: left;
height: 60px;
background-color: #f3efef;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
margin-top: 20px;
width: 100%;
position:fixed;
bottom: 0;
left: 0;
}
.fsend .fsendcon{
margin-left: auto;
padding: 12px;
background-color: #fff;
min-height: 60px;
width: 1200px;
margin-left: auto;
margin-right: auto;
}
@import url('@/assets/sfilecompare.css');
</style>

329
vue/afvue/yarn.lock

@ -4,101 +4,91 @@
"@babel/helper-string-parser@^7.27.1":
version "7.27.1"
resolved "https://registry.npmmirror.com/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz#54da796097ab19ce67ed9f88b47bb2ec49367687"
resolved "https://registry.npmmirror.com/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz"
integrity sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==
"@babel/helper-validator-identifier@^7.27.1":
version "7.27.1"
resolved "https://registry.npmmirror.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.27.1.tgz#a7054dcc145a967dd4dc8fee845a57c1316c9df8"
resolved "https://registry.npmmirror.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.27.1.tgz"
integrity sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==
"@babel/parser@^7.23.5":
version "7.28.3"
resolved "https://registry.npmmirror.com/@babel/parser/-/parser-7.28.3.tgz#d2d25b814621bca5fe9d172bc93792547e7a2a71"
integrity sha512-7+Ey1mAgYqFAx2h0RuoxcQT5+MlG3GTV0TQrgr7/ZliKsm/MNDxVVutlWaziMq7wJNAz8MTqz55XLpWvva6StA==
version "7.28.0"
resolved "https://registry.npmmirror.com/@babel/parser/-/parser-7.28.0.tgz"
integrity sha512-jVZGvOxOuNSsuQuLRTh13nU0AogFlw32w/MT+LV6D3sP5WdbW61E77RnkbaO2dUvmPAYrBDJXGn5gGS6tH4j8g==
dependencies:
"@babel/types" "^7.28.2"
"@babel/types" "^7.28.0"
"@babel/standalone@^7.20.0":
version "7.28.3"
resolved "https://registry.npmmirror.com/@babel/standalone/-/standalone-7.28.3.tgz#0e126deef7b88b08481c233c7ac0e94c9629d7cd"
integrity sha512-VHmaaU23OkxShTtkwXlte7/uHDK8v55J9YLMqlucjnYujeB9YgrYCHU6LREqUegTVq+/KlLgjoUu8lbeI3XQPA==
version "7.28.2"
resolved "https://registry.npmmirror.com/@babel/standalone/-/standalone-7.28.2.tgz"
integrity sha512-1kjA8XzBRN68HoDDYKP38bucHtxYWCIX8XdYwe1drRNUOjOVNt8EMy9jiE6UwaGFfU7NOHCG+C8KgBc9CR08nA==
"@babel/types@^7.28.2":
"@babel/types@^7.28.0":
version "7.28.2"
resolved "https://registry.npmmirror.com/@babel/types/-/types-7.28.2.tgz#da9db0856a9a88e0a13b019881d7513588cf712b"
resolved "https://registry.npmmirror.com/@babel/types/-/types-7.28.2.tgz"
integrity sha512-ruv7Ae4J5dUYULmeXw1gmb7rYRz57OWCPM57pHojnLq/3Z1CK2lNSLTCVjxVk1F/TZHwOZZrOWi0ur95BbLxNQ==
dependencies:
"@babel/helper-string-parser" "^7.27.1"
"@babel/helper-validator-identifier" "^7.27.1"
"@esbuild/android-arm@0.15.18":
version "0.15.18"
resolved "https://registry.npmmirror.com/@esbuild/android-arm/-/android-arm-0.15.18.tgz#266d40b8fdcf87962df8af05b76219bc786b4f80"
integrity sha512-5GT+kcs2WVGjVs7+boataCkO5Fg0y4kCjzkB5bAip7H4jfnOS3dA6KPiww9W1OEKTKeAcUVhdZGvgI65OXmUnw==
"@esbuild/linux-loong64@0.15.18":
version "0.15.18"
resolved "https://registry.npmmirror.com/@esbuild/linux-loong64/-/linux-loong64-0.15.18.tgz#128b76ecb9be48b60cf5cfc1c63a4f00691a3239"
integrity sha512-L4jVKS82XVhw2nvzLg/19ClLWg0y27ulRwuP7lcyL6AbUWB5aPglXY3M21mauDQMDfRLs8cQmeT03r/+X3cZYQ==
"@jridgewell/gen-mapping@^0.3.5":
version "0.3.13"
resolved "https://registry.npmmirror.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz#6342a19f44347518c93e43b1ac69deb3c4656a1f"
integrity sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==
version "0.3.12"
resolved "https://registry.npmmirror.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.12.tgz"
integrity sha512-OuLGC46TjB5BbN1dH8JULVVZY4WTdkF7tV9Ys6wLL1rubZnCMstOhNHueU5bLCrnRuDhKPDM4g6sw4Bel5Gzqg==
dependencies:
"@jridgewell/sourcemap-codec" "^1.5.0"
"@jridgewell/trace-mapping" "^0.3.24"
"@jridgewell/resolve-uri@^3.1.0":
version "3.1.2"
resolved "https://registry.npmmirror.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz#7a0ee601f60f99a20c7c7c5ff0c80388c1189bd6"
resolved "https://registry.npmmirror.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz"
integrity sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==
"@jridgewell/source-map@^0.3.3":
version "0.3.11"
resolved "https://registry.npmmirror.com/@jridgewell/source-map/-/source-map-0.3.11.tgz#b21835cbd36db656b857c2ad02ebd413cc13a9ba"
integrity sha512-ZMp1V8ZFcPG5dIWnQLr3NSI1MiCU7UETdS/A0G8V/XWHvJv3ZsFqutJn1Y5RPmAPX6F3BiE397OqveU/9NCuIA==
version "0.3.10"
resolved "https://registry.npmmirror.com/@jridgewell/source-map/-/source-map-0.3.10.tgz"
integrity sha512-0pPkgz9dY+bijgistcTTJ5mR+ocqRXLuhXHYdzoMmmoJ2C9S46RCm2GMUbatPEUK9Yjy26IrAy8D/M00lLkv+Q==
dependencies:
"@jridgewell/gen-mapping" "^0.3.5"
"@jridgewell/trace-mapping" "^0.3.25"
"@jridgewell/sourcemap-codec@^1.4.14", "@jridgewell/sourcemap-codec@^1.5.0":
version "1.5.5"
resolved "https://registry.npmmirror.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz#6912b00d2c631c0d15ce1a7ab57cd657f2a8f8ba"
integrity sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==
version "1.5.4"
resolved "https://registry.npmmirror.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.4.tgz"
integrity sha512-VT2+G1VQs/9oz078bLrYbecdZKs912zQlkelYpuf+SXF+QvZDYJlbx/LSx+meSAwdDFnF8FVXW92AVjjkVmgFw==
"@jridgewell/trace-mapping@^0.3.24", "@jridgewell/trace-mapping@^0.3.25":
version "0.3.30"
resolved "https://registry.npmmirror.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.30.tgz#4a76c4daeee5df09f5d3940e087442fb36ce2b99"
integrity sha512-GQ7Nw5G2lTu/BtHTKfXhKHok2WGetd4XYcVKGx00SjAk8GMwgJM3zr6zORiPGuOE+/vkc90KtTosSSvaCjKb2Q==
version "0.3.29"
resolved "https://registry.npmmirror.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.29.tgz"
integrity sha512-uw6guiW/gcAGPDhLmd77/6lW8QLeiV5RUTsAX46Db6oLhGaVj4lhnPwb184s1bkc8kdVg/+h988dro8GRDpmYQ==
dependencies:
"@jridgewell/resolve-uri" "^3.1.0"
"@jridgewell/sourcemap-codec" "^1.4.14"
"@protobufjs/aspromise@^1.1.1", "@protobufjs/aspromise@^1.1.2":
version "1.1.2"
resolved "https://registry.npmmirror.com/@protobufjs/aspromise/-/aspromise-1.1.2.tgz#9b8b0cc663d669a7d8f6f5d0893a14d348f30fbf"
resolved "https://registry.npmmirror.com/@protobufjs/aspromise/-/aspromise-1.1.2.tgz"
integrity sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==
"@protobufjs/base64@^1.1.2":
version "1.1.2"
resolved "https://registry.npmmirror.com/@protobufjs/base64/-/base64-1.1.2.tgz#4c85730e59b9a1f1f349047dbf24296034bb2735"
resolved "https://registry.npmmirror.com/@protobufjs/base64/-/base64-1.1.2.tgz"
integrity sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==
"@protobufjs/codegen@^2.0.4":
version "2.0.4"
resolved "https://registry.npmmirror.com/@protobufjs/codegen/-/codegen-2.0.4.tgz#7ef37f0d010fb028ad1ad59722e506d9262815cb"
resolved "https://registry.npmmirror.com/@protobufjs/codegen/-/codegen-2.0.4.tgz"
integrity sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==
"@protobufjs/eventemitter@^1.1.0":
version "1.1.0"
resolved "https://registry.npmmirror.com/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz#355cbc98bafad5978f9ed095f397621f1d066b70"
resolved "https://registry.npmmirror.com/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz"
integrity sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q==
"@protobufjs/fetch@^1.1.0":
version "1.1.0"
resolved "https://registry.npmmirror.com/@protobufjs/fetch/-/fetch-1.1.0.tgz#ba99fb598614af65700c1619ff06d454b0d84c45"
resolved "https://registry.npmmirror.com/@protobufjs/fetch/-/fetch-1.1.0.tgz"
integrity sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ==
dependencies:
"@protobufjs/aspromise" "^1.1.1"
@ -106,39 +96,39 @@
"@protobufjs/float@^1.0.2":
version "1.0.2"
resolved "https://registry.npmmirror.com/@protobufjs/float/-/float-1.0.2.tgz#5e9e1abdcb73fc0a7cb8b291df78c8cbd97b87d1"
resolved "https://registry.npmmirror.com/@protobufjs/float/-/float-1.0.2.tgz"
integrity sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==
"@protobufjs/inquire@^1.1.0":
version "1.1.0"
resolved "https://registry.npmmirror.com/@protobufjs/inquire/-/inquire-1.1.0.tgz#ff200e3e7cf2429e2dcafc1140828e8cc638f089"
resolved "https://registry.npmmirror.com/@protobufjs/inquire/-/inquire-1.1.0.tgz"
integrity sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q==
"@protobufjs/path@^1.1.2":
version "1.1.2"
resolved "https://registry.npmmirror.com/@protobufjs/path/-/path-1.1.2.tgz#6cc2b20c5c9ad6ad0dccfd21ca7673d8d7fbf68d"
resolved "https://registry.npmmirror.com/@protobufjs/path/-/path-1.1.2.tgz"
integrity sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==
"@protobufjs/pool@^1.1.0":
version "1.1.0"
resolved "https://registry.npmmirror.com/@protobufjs/pool/-/pool-1.1.0.tgz#09fd15f2d6d3abfa9b65bc366506d6ad7846ff54"
resolved "https://registry.npmmirror.com/@protobufjs/pool/-/pool-1.1.0.tgz"
integrity sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==
"@protobufjs/utf8@^1.1.0":
version "1.1.0"
resolved "https://registry.npmmirror.com/@protobufjs/utf8/-/utf8-1.1.0.tgz#a777360b5b39a1a2e5106f8e858f2fd2d060c570"
resolved "https://registry.npmmirror.com/@protobufjs/utf8/-/utf8-1.1.0.tgz"
integrity sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==
"@types/node@>=13.7.0":
"@types/node@>= 14", "@types/node@>=13.7.0":
version "24.3.0"
resolved "https://registry.npmmirror.com/@types/node/-/node-24.3.0.tgz#89b09f45cb9a8ee69466f18ee5864e4c3eb84dec"
resolved "https://registry.npmmirror.com/@types/node/-/node-24.3.0.tgz"
integrity sha512-aPTXCrfwnDLj4VvXrm+UUCQjNEvJgNA8s5F1cvwQU+3KNltTOkBm1j30uNLyqqPNe7gE3KFzImYoZEfLhp4Yow==
dependencies:
undici-types "~7.10.0"
"@vitejs/plugin-legacy@^2.0.0":
version "2.3.1"
resolved "https://registry.npmmirror.com/@vitejs/plugin-legacy/-/plugin-legacy-2.3.1.tgz#44d8e608e66ef03ff82ae176588c7a621d56c524"
resolved "https://registry.npmmirror.com/@vitejs/plugin-legacy/-/plugin-legacy-2.3.1.tgz"
integrity sha512-J5KaGBlSt2tEYPVjM/C8dA6DkRzkFkbPe+Xb4IX5G+XOV5OGbVAfkMjKywdrkO3gGynO8S98i71Lmsff4cWkCQ==
dependencies:
"@babel/standalone" "^7.20.0"
@ -149,12 +139,12 @@
"@vitejs/plugin-vue2@^1.1.2":
version "1.1.2"
resolved "https://registry.npmmirror.com/@vitejs/plugin-vue2/-/plugin-vue2-1.1.2.tgz#891f0acc5a6a2b4886a74cb8d6359d42f19f968a"
resolved "https://registry.npmmirror.com/@vitejs/plugin-vue2/-/plugin-vue2-1.1.2.tgz"
integrity sha512-y6OEA+2UdJ0xrEQHodq20v9r3SpS62IOHrgN92JPLvVpNkhcissu7yvD5PXMzMESyazj0XNWGsc8UQk8+mVrjQ==
"@vue/compiler-sfc@2.7.16":
version "2.7.16"
resolved "https://registry.npmmirror.com/@vue/compiler-sfc/-/compiler-sfc-2.7.16.tgz#ff81711a0fac9c68683d8bb00b63f857de77dc83"
resolved "https://registry.npmmirror.com/@vue/compiler-sfc/-/compiler-sfc-2.7.16.tgz"
integrity sha512-KWhJ9k5nXuNtygPU7+t1rX6baZeqOYLEforUPjgNDBnLicfHCoi48H87Q8XyLZOrNNsmhuwKqtpDQWjEFe6Ekg==
dependencies:
"@babel/parser" "^7.23.5"
@ -165,22 +155,22 @@
"@vue/devtools-api@^6.6.3":
version "6.6.4"
resolved "https://registry.npmmirror.com/@vue/devtools-api/-/devtools-api-6.6.4.tgz#cbe97fe0162b365edc1dba80e173f90492535343"
resolved "https://registry.npmmirror.com/@vue/devtools-api/-/devtools-api-6.6.4.tgz"
integrity sha512-sGhTPMuXqZ1rVOk32RylztWkfXTRhuS7vgAKv0zjqk8gbsHkJ7xfFf+jbySxt7tWObEJwyKaHMikV/WGDiQm8g==
acorn@^8.14.0:
version "8.15.0"
resolved "https://registry.npmmirror.com/acorn/-/acorn-8.15.0.tgz#a360898bc415edaac46c8241f6383975b930b816"
resolved "https://registry.npmmirror.com/acorn/-/acorn-8.15.0.tgz"
integrity sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==
asynckit@^0.4.0:
version "0.4.0"
resolved "https://registry.npmmirror.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79"
resolved "https://registry.npmmirror.com/asynckit/-/asynckit-0.4.0.tgz"
integrity sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==
axios@^1.11.0:
version "1.11.0"
resolved "https://registry.npmmirror.com/axios/-/axios-1.11.0.tgz#c2ec219e35e414c025b2095e8b8280278478fdb6"
resolved "https://registry.npmmirror.com/axios/-/axios-1.11.0.tgz"
integrity sha512-1Lx3WLFQWm3ooKDYZD1eXmoGO9fxYQjrycfHFC8P0sCfQVXyROp0p9PFWBehewBOdCwHc+f/b8I0fMto5eSfwA==
dependencies:
follow-redirects "^1.15.6"
@ -189,12 +179,12 @@ axios@^1.11.0:
buffer-from@^1.0.0:
version "1.1.2"
resolved "https://registry.npmmirror.com/buffer-from/-/buffer-from-1.1.2.tgz#2b146a6fd72e80b4f55d255f35ed59a3a9a41bd5"
resolved "https://registry.npmmirror.com/buffer-from/-/buffer-from-1.1.2.tgz"
integrity sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==
call-bind-apply-helpers@^1.0.1, call-bind-apply-helpers@^1.0.2:
version "1.0.2"
resolved "https://registry.npmmirror.com/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz#4b5428c222be985d79c3d82657479dbe0b59b2d6"
resolved "https://registry.npmmirror.com/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz"
integrity sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==
dependencies:
es-errors "^1.3.0"
@ -202,34 +192,34 @@ call-bind-apply-helpers@^1.0.1, call-bind-apply-helpers@^1.0.2:
combined-stream@^1.0.8:
version "1.0.8"
resolved "https://registry.npmmirror.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f"
resolved "https://registry.npmmirror.com/combined-stream/-/combined-stream-1.0.8.tgz"
integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==
dependencies:
delayed-stream "~1.0.0"
commander@^2.20.0:
version "2.20.3"
resolved "https://registry.npmmirror.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33"
resolved "https://registry.npmmirror.com/commander/-/commander-2.20.3.tgz"
integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==
core-js@^3.26.0:
version "3.45.1"
resolved "https://registry.npmmirror.com/core-js/-/core-js-3.45.1.tgz#5810e04a1b4e9bc5ddaa4dd12e702ff67300634d"
integrity sha512-L4NPsJlCfZsPeXukyzHFlg/i7IIVwHSItR0wg0FLNqYClJ4MQYTYLbC7EkjKYRLZF2iof2MUgN0EGy7MdQFChg==
version "3.45.0"
resolved "https://registry.npmmirror.com/core-js/-/core-js-3.45.0.tgz"
integrity sha512-c2KZL9lP4DjkN3hk/an4pWn5b5ZefhRJnAc42n6LJ19kSnbeRbdQZE5dSeE2LBol1OwJD3X1BQvFTAsa8ReeDA==
csstype@^3.1.0:
version "3.1.3"
resolved "https://registry.npmmirror.com/csstype/-/csstype-3.1.3.tgz#d80ff294d114fb0e6ac500fbf85b60137d7eff81"
resolved "https://registry.npmmirror.com/csstype/-/csstype-3.1.3.tgz"
integrity sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==
delayed-stream@~1.0.0:
version "1.0.0"
resolved "https://registry.npmmirror.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619"
resolved "https://registry.npmmirror.com/delayed-stream/-/delayed-stream-1.0.0.tgz"
integrity sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==
dunder-proto@^1.0.1:
version "1.0.1"
resolved "https://registry.npmmirror.com/dunder-proto/-/dunder-proto-1.0.1.tgz#d7ae667e1dc83482f8b70fd0f6eefc50da30f58a"
resolved "https://registry.npmmirror.com/dunder-proto/-/dunder-proto-1.0.1.tgz"
integrity sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==
dependencies:
call-bind-apply-helpers "^1.0.1"
@ -238,24 +228,24 @@ dunder-proto@^1.0.1:
es-define-property@^1.0.1:
version "1.0.1"
resolved "https://registry.npmmirror.com/es-define-property/-/es-define-property-1.0.1.tgz#983eb2f9a6724e9303f61addf011c72e09e0b0fa"
resolved "https://registry.npmmirror.com/es-define-property/-/es-define-property-1.0.1.tgz"
integrity sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==
es-errors@^1.3.0:
version "1.3.0"
resolved "https://registry.npmmirror.com/es-errors/-/es-errors-1.3.0.tgz#05f75a25dab98e4fb1dcd5e1472c0546d5057c8f"
resolved "https://registry.npmmirror.com/es-errors/-/es-errors-1.3.0.tgz"
integrity sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==
es-object-atoms@^1.0.0, es-object-atoms@^1.1.1:
version "1.1.1"
resolved "https://registry.npmmirror.com/es-object-atoms/-/es-object-atoms-1.1.1.tgz#1c4f2c4837327597ce69d2ca190a7fdd172338c1"
resolved "https://registry.npmmirror.com/es-object-atoms/-/es-object-atoms-1.1.1.tgz"
integrity sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==
dependencies:
es-errors "^1.3.0"
es-set-tostringtag@^2.1.0:
version "2.1.0"
resolved "https://registry.npmmirror.com/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz#f31dbbe0c183b00a6d26eb6325c810c0fd18bd4d"
resolved "https://registry.npmmirror.com/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz"
integrity sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==
dependencies:
es-errors "^1.3.0"
@ -263,109 +253,14 @@ es-set-tostringtag@^2.1.0:
has-tostringtag "^1.0.2"
hasown "^2.0.2"
esbuild-android-64@0.15.18:
version "0.15.18"
resolved "https://registry.npmmirror.com/esbuild-android-64/-/esbuild-android-64-0.15.18.tgz#20a7ae1416c8eaade917fb2453c1259302c637a5"
integrity sha512-wnpt3OXRhcjfIDSZu9bnzT4/TNTDsOUvip0foZOUBG7QbSt//w3QV4FInVJxNhKc/ErhUxc5z4QjHtMi7/TbgA==
esbuild-android-arm64@0.15.18:
version "0.15.18"
resolved "https://registry.npmmirror.com/esbuild-android-arm64/-/esbuild-android-arm64-0.15.18.tgz#9cc0ec60581d6ad267568f29cf4895ffdd9f2f04"
integrity sha512-G4xu89B8FCzav9XU8EjsXacCKSG2FT7wW9J6hOc18soEHJdtWu03L3TQDGf0geNxfLTtxENKBzMSq9LlbjS8OQ==
esbuild-darwin-64@0.15.18:
version "0.15.18"
resolved "https://registry.npmmirror.com/esbuild-darwin-64/-/esbuild-darwin-64-0.15.18.tgz#428e1730ea819d500808f220fbc5207aea6d4410"
integrity sha512-2WAvs95uPnVJPuYKP0Eqx+Dl/jaYseZEUUT1sjg97TJa4oBtbAKnPnl3b5M9l51/nbx7+QAEtuummJZW0sBEmg==
esbuild-darwin-arm64@0.15.18:
version "0.15.18"
resolved "https://registry.npmmirror.com/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.15.18.tgz#b6dfc7799115a2917f35970bfbc93ae50256b337"
integrity sha512-tKPSxcTJ5OmNb1btVikATJ8NftlyNlc8BVNtyT/UAr62JFOhwHlnoPrhYWz09akBLHI9nElFVfWSTSRsrZiDUA==
esbuild-freebsd-64@0.15.18:
version "0.15.18"
resolved "https://registry.npmmirror.com/esbuild-freebsd-64/-/esbuild-freebsd-64-0.15.18.tgz#4e190d9c2d1e67164619ae30a438be87d5eedaf2"
integrity sha512-TT3uBUxkteAjR1QbsmvSsjpKjOX6UkCstr8nMr+q7zi3NuZ1oIpa8U41Y8I8dJH2fJgdC3Dj3CXO5biLQpfdZA==
esbuild-freebsd-arm64@0.15.18:
version "0.15.18"
resolved "https://registry.npmmirror.com/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.15.18.tgz#18a4c0344ee23bd5a6d06d18c76e2fd6d3f91635"
integrity sha512-R/oVr+X3Tkh+S0+tL41wRMbdWtpWB8hEAMsOXDumSSa6qJR89U0S/PpLXrGF7Wk/JykfpWNokERUpCeHDl47wA==
esbuild-linux-32@0.15.18:
version "0.15.18"
resolved "https://registry.npmmirror.com/esbuild-linux-32/-/esbuild-linux-32-0.15.18.tgz#9a329731ee079b12262b793fb84eea762e82e0ce"
integrity sha512-lphF3HiCSYtaa9p1DtXndiQEeQDKPl9eN/XNoBf2amEghugNuqXNZA/ZovthNE2aa4EN43WroO0B85xVSjYkbg==
esbuild-linux-64@0.15.18:
version "0.15.18"
resolved "https://registry.npmmirror.com/esbuild-linux-64/-/esbuild-linux-64-0.15.18.tgz#532738075397b994467b514e524aeb520c191b6c"
integrity sha512-hNSeP97IviD7oxLKFuii5sDPJ+QHeiFTFLoLm7NZQligur8poNOWGIgpQ7Qf8Balb69hptMZzyOBIPtY09GZYw==
esbuild-linux-arm64@0.15.18:
version "0.15.18"
resolved "https://registry.npmmirror.com/esbuild-linux-arm64/-/esbuild-linux-arm64-0.15.18.tgz#5372e7993ac2da8f06b2ba313710d722b7a86e5d"
integrity sha512-54qr8kg/6ilcxd+0V3h9rjT4qmjc0CccMVWrjOEM/pEcUzt8X62HfBSeZfT2ECpM7104mk4yfQXkosY8Quptug==
esbuild-linux-arm@0.15.18:
version "0.15.18"
resolved "https://registry.npmmirror.com/esbuild-linux-arm/-/esbuild-linux-arm-0.15.18.tgz#e734aaf259a2e3d109d4886c9e81ec0f2fd9a9cc"
integrity sha512-UH779gstRblS4aoS2qpMl3wjg7U0j+ygu3GjIeTonCcN79ZvpPee12Qun3vcdxX+37O5LFxz39XeW2I9bybMVA==
esbuild-linux-mips64le@0.15.18:
version "0.15.18"
resolved "https://registry.npmmirror.com/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.15.18.tgz#c0487c14a9371a84eb08fab0e1d7b045a77105eb"
integrity sha512-Mk6Ppwzzz3YbMl/ZZL2P0q1tnYqh/trYZ1VfNP47C31yT0K8t9s7Z077QrDA/guU60tGNp2GOwCQnp+DYv7bxQ==
esbuild-linux-ppc64le@0.15.18:
version "0.15.18"
resolved "https://registry.npmmirror.com/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.15.18.tgz#af048ad94eed0ce32f6d5a873f7abe9115012507"
integrity sha512-b0XkN4pL9WUulPTa/VKHx2wLCgvIAbgwABGnKMY19WhKZPT+8BxhZdqz6EgkqCLld7X5qiCY2F/bfpUUlnFZ9w==
esbuild-linux-riscv64@0.15.18:
version "0.15.18"
resolved "https://registry.npmmirror.com/esbuild-linux-riscv64/-/esbuild-linux-riscv64-0.15.18.tgz#423ed4e5927bd77f842bd566972178f424d455e6"
integrity sha512-ba2COaoF5wL6VLZWn04k+ACZjZ6NYniMSQStodFKH/Pu6RxzQqzsmjR1t9QC89VYJxBeyVPTaHuBMCejl3O/xg==
esbuild-linux-s390x@0.15.18:
version "0.15.18"
resolved "https://registry.npmmirror.com/esbuild-linux-s390x/-/esbuild-linux-s390x-0.15.18.tgz#21d21eaa962a183bfb76312e5a01cc5ae48ce8eb"
integrity sha512-VbpGuXEl5FCs1wDVp93O8UIzl3ZrglgnSQ+Hu79g7hZu6te6/YHgVJxCM2SqfIila0J3k0csfnf8VD2W7u2kzQ==
esbuild-netbsd-64@0.15.18:
version "0.15.18"
resolved "https://registry.npmmirror.com/esbuild-netbsd-64/-/esbuild-netbsd-64-0.15.18.tgz#ae75682f60d08560b1fe9482bfe0173e5110b998"
integrity sha512-98ukeCdvdX7wr1vUYQzKo4kQ0N2p27H7I11maINv73fVEXt2kyh4K4m9f35U1K43Xc2QGXlzAw0K9yoU7JUjOg==
esbuild-openbsd-64@0.15.18:
version "0.15.18"
resolved "https://registry.npmmirror.com/esbuild-openbsd-64/-/esbuild-openbsd-64-0.15.18.tgz#79591a90aa3b03e4863f93beec0d2bab2853d0a8"
integrity sha512-yK5NCcH31Uae076AyQAXeJzt/vxIo9+omZRKj1pauhk3ITuADzuOx5N2fdHrAKPxN+zH3w96uFKlY7yIn490xQ==
esbuild-sunos-64@0.15.18:
version "0.15.18"
resolved "https://registry.npmmirror.com/esbuild-sunos-64/-/esbuild-sunos-64-0.15.18.tgz#fd528aa5da5374b7e1e93d36ef9b07c3dfed2971"
integrity sha512-On22LLFlBeLNj/YF3FT+cXcyKPEI263nflYlAhz5crxtp3yRG1Ugfr7ITyxmCmjm4vbN/dGrb/B7w7U8yJR9yw==
esbuild-windows-32@0.15.18:
version "0.15.18"
resolved "https://registry.npmmirror.com/esbuild-windows-32/-/esbuild-windows-32-0.15.18.tgz#0e92b66ecdf5435a76813c4bc5ccda0696f4efc3"
integrity sha512-o+eyLu2MjVny/nt+E0uPnBxYuJHBvho8vWsC2lV61A7wwTWC3jkN2w36jtA+yv1UgYkHRihPuQsL23hsCYGcOQ==
esbuild-windows-64@0.15.18:
version "0.15.18"
resolved "https://registry.npmmirror.com/esbuild-windows-64/-/esbuild-windows-64-0.15.18.tgz#0fc761d785414284fc408e7914226d33f82420d0"
resolved "https://registry.npmmirror.com/esbuild-windows-64/-/esbuild-windows-64-0.15.18.tgz"
integrity sha512-qinug1iTTaIIrCorAUjR0fcBk24fjzEedFYhhispP8Oc7SFvs+XeW3YpAKiKp8dRpizl4YYAhxMjlftAMJiaUw==
esbuild-windows-arm64@0.15.18:
version "0.15.18"
resolved "https://registry.npmmirror.com/esbuild-windows-arm64/-/esbuild-windows-arm64-0.15.18.tgz#5b5bdc56d341d0922ee94965c89ee120a6a86eb7"
integrity sha512-q9bsYzegpZcLziq0zgUi5KqGVtfhjxGbnksaBFYmWLxeV/S1fK4OLdq2DFYnXcLMjlZw2L0jLsk1eGoB522WXQ==
esbuild@^0.15.9:
version "0.15.18"
resolved "https://registry.npmmirror.com/esbuild/-/esbuild-0.15.18.tgz#ea894adaf3fbc036d32320a00d4d6e4978a2f36d"
resolved "https://registry.npmmirror.com/esbuild/-/esbuild-0.15.18.tgz"
integrity sha512-x/R72SmW3sSFRm5zrrIjAhCeQSAWoni3CmHEqfQrZIQTM3lVCdehdwuIqaOtfC2slvpdlLa62GYoN8SxT23m6Q==
optionalDependencies:
"@esbuild/android-arm" "0.15.18"
@ -393,17 +288,17 @@ esbuild@^0.15.9:
event-source-polyfill@^1.0.31:
version "1.0.31"
resolved "https://registry.npmmirror.com/event-source-polyfill/-/event-source-polyfill-1.0.31.tgz#45fb0a6fc1375b2ba597361ba4287ffec5bf2e0c"
resolved "https://registry.npmmirror.com/event-source-polyfill/-/event-source-polyfill-1.0.31.tgz"
integrity sha512-4IJSItgS/41IxN5UVAVuAyczwZF7ZIEsM1XAoUzIHA6A+xzusEZUutdXz2Nr+MQPLxfTiCvqE79/C8HT8fKFvA==
follow-redirects@^1.15.6:
version "1.15.11"
resolved "https://registry.npmmirror.com/follow-redirects/-/follow-redirects-1.15.11.tgz#777d73d72a92f8ec4d2e410eb47352a56b8e8340"
resolved "https://registry.npmmirror.com/follow-redirects/-/follow-redirects-1.15.11.tgz"
integrity sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==
form-data@^4.0.4:
version "4.0.4"
resolved "https://registry.npmmirror.com/form-data/-/form-data-4.0.4.tgz#784cdcce0669a9d68e94d11ac4eea98088edd2c4"
resolved "https://registry.npmmirror.com/form-data/-/form-data-4.0.4.tgz"
integrity sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow==
dependencies:
asynckit "^0.4.0"
@ -412,19 +307,14 @@ form-data@^4.0.4:
hasown "^2.0.2"
mime-types "^2.1.12"
fsevents@~2.3.2:
version "2.3.3"
resolved "https://registry.npmmirror.com/fsevents/-/fsevents-2.3.3.tgz#cac6407785d03675a2a5e1a5305c697b347d90d6"
integrity sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==
function-bind@^1.1.2:
version "1.1.2"
resolved "https://registry.npmmirror.com/function-bind/-/function-bind-1.1.2.tgz#2c02d864d97f3ea6c8830c464cbd11ab6eab7a1c"
resolved "https://registry.npmmirror.com/function-bind/-/function-bind-1.1.2.tgz"
integrity sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==
get-intrinsic@^1.2.6:
version "1.3.0"
resolved "https://registry.npmmirror.com/get-intrinsic/-/get-intrinsic-1.3.0.tgz#743f0e3b6964a93a5491ed1bffaae054d7f98d01"
resolved "https://registry.npmmirror.com/get-intrinsic/-/get-intrinsic-1.3.0.tgz"
integrity sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==
dependencies:
call-bind-apply-helpers "^1.0.2"
@ -440,7 +330,7 @@ get-intrinsic@^1.2.6:
get-proto@^1.0.1:
version "1.0.1"
resolved "https://registry.npmmirror.com/get-proto/-/get-proto-1.0.1.tgz#150b3f2743869ef3e851ec0c49d15b1d14d00ee1"
resolved "https://registry.npmmirror.com/get-proto/-/get-proto-1.0.1.tgz"
integrity sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==
dependencies:
dunder-proto "^1.0.1"
@ -448,87 +338,92 @@ get-proto@^1.0.1:
gopd@^1.2.0:
version "1.2.0"
resolved "https://registry.npmmirror.com/gopd/-/gopd-1.2.0.tgz#89f56b8217bdbc8802bd299df6d7f1081d7e51a1"
resolved "https://registry.npmmirror.com/gopd/-/gopd-1.2.0.tgz"
integrity sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==
has-symbols@^1.0.3, has-symbols@^1.1.0:
version "1.1.0"
resolved "https://registry.npmmirror.com/has-symbols/-/has-symbols-1.1.0.tgz#fc9c6a783a084951d0b971fe1018de813707a338"
resolved "https://registry.npmmirror.com/has-symbols/-/has-symbols-1.1.0.tgz"
integrity sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==
has-tostringtag@^1.0.2:
version "1.0.2"
resolved "https://registry.npmmirror.com/has-tostringtag/-/has-tostringtag-1.0.2.tgz#2cdc42d40bef2e5b4eeab7c01a73c54ce7ab5abc"
resolved "https://registry.npmmirror.com/has-tostringtag/-/has-tostringtag-1.0.2.tgz"
integrity sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==
dependencies:
has-symbols "^1.0.3"
hasown@^2.0.2:
version "2.0.2"
resolved "https://registry.npmmirror.com/hasown/-/hasown-2.0.2.tgz#003eaf91be7adc372e84ec59dc37252cedb80003"
resolved "https://registry.npmmirror.com/hasown/-/hasown-2.0.2.tgz"
integrity sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==
dependencies:
function-bind "^1.1.2"
is-core-module@^2.16.0:
version "2.16.1"
resolved "https://registry.npmmirror.com/is-core-module/-/is-core-module-2.16.1.tgz#2a98801a849f43e2add644fbb6bc6229b19a4ef4"
resolved "https://registry.npmmirror.com/is-core-module/-/is-core-module-2.16.1.tgz"
integrity sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==
dependencies:
hasown "^2.0.2"
lodash@^4.17.21:
version "4.17.21"
resolved "https://registry.npmmirror.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c"
resolved "https://registry.npmmirror.com/lodash/-/lodash-4.17.21.tgz"
integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==
long@^5.0.0:
version "5.3.2"
resolved "https://registry.npmmirror.com/long/-/long-5.3.2.tgz#1d84463095999262d7d7b7f8bfd4a8cc55167f83"
resolved "https://registry.npmmirror.com/long/-/long-5.3.2.tgz"
integrity sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA==
magic-string@^0.26.7:
version "0.26.7"
resolved "https://registry.npmmirror.com/magic-string/-/magic-string-0.26.7.tgz#caf7daf61b34e9982f8228c4527474dac8981d6f"
resolved "https://registry.npmmirror.com/magic-string/-/magic-string-0.26.7.tgz"
integrity sha512-hX9XH3ziStPoPhJxLq1syWuZMxbDvGNbVchfrdCtanC7D13888bMFow61x8axrx+GfHLtVeAx2kxL7tTGRl+Ow==
dependencies:
sourcemap-codec "^1.4.8"
math-intrinsics@^1.1.0:
version "1.1.0"
resolved "https://registry.npmmirror.com/math-intrinsics/-/math-intrinsics-1.1.0.tgz#a0dd74be81e2aa5c2f27e65ce283605ee4e2b7f9"
resolved "https://registry.npmmirror.com/math-intrinsics/-/math-intrinsics-1.1.0.tgz"
integrity sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==
mime-db@1.52.0:
version "1.52.0"
resolved "https://registry.npmmirror.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70"
resolved "https://registry.npmmirror.com/mime-db/-/mime-db-1.52.0.tgz"
integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==
mime-types@^2.1.12:
version "2.1.35"
resolved "https://registry.npmmirror.com/mime-types/-/mime-types-2.1.35.tgz#381a871b62a734450660ae3deee44813f70d959a"
resolved "https://registry.npmmirror.com/mime-types/-/mime-types-2.1.35.tgz"
integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==
dependencies:
mime-db "1.52.0"
nanoid@^3.3.11:
version "3.3.11"
resolved "https://registry.npmmirror.com/nanoid/-/nanoid-3.3.11.tgz#4f4f112cefbe303202f2199838128936266d185b"
resolved "https://registry.npmmirror.com/nanoid/-/nanoid-3.3.11.tgz"
integrity sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==
nprogress@^0.2.0:
version "0.2.0"
resolved "https://registry.npmmirror.com/nprogress/-/nprogress-0.2.0.tgz"
integrity sha512-I19aIingLgR1fmhftnbWWO3dXc0hSxqHQHQb3H8m+K3TnEn/iSeTZZOyvKXWqQESMwuUVnatlCnZdLBZZt2VSA==
path-parse@^1.0.7:
version "1.0.7"
resolved "https://registry.npmmirror.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735"
resolved "https://registry.npmmirror.com/path-parse/-/path-parse-1.0.7.tgz"
integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==
picocolors@^1.1.1:
version "1.1.1"
resolved "https://registry.npmmirror.com/picocolors/-/picocolors-1.1.1.tgz#3d321af3eab939b083c8f929a1d12cda81c26b6b"
resolved "https://registry.npmmirror.com/picocolors/-/picocolors-1.1.1.tgz"
integrity sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==
pinia@^2.0.16:
version "2.3.1"
resolved "https://registry.npmmirror.com/pinia/-/pinia-2.3.1.tgz#54c476675b72f5abcfafa24a7582531ea8c23d94"
resolved "https://registry.npmmirror.com/pinia/-/pinia-2.3.1.tgz"
integrity sha512-khUlZSwt9xXCaTbbxFYBKDc/bWAGWJjOgvxETwkTN7KRm66EeT1ZdZj6i2ceh9sP2Pzqsbc704r2yngBrxBVug==
dependencies:
"@vue/devtools-api" "^6.6.3"
@ -536,7 +431,7 @@ pinia@^2.0.16:
postcss@^8.4.14, postcss@^8.4.18:
version "8.5.6"
resolved "https://registry.npmmirror.com/postcss/-/postcss-8.5.6.tgz#2825006615a619b4f62a9e7426cc120b349a8f3c"
resolved "https://registry.npmmirror.com/postcss/-/postcss-8.5.6.tgz"
integrity sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==
dependencies:
nanoid "^3.3.11"
@ -545,12 +440,12 @@ postcss@^8.4.14, postcss@^8.4.18:
"prettier@^1.18.2 || ^2.0.0":
version "2.8.8"
resolved "https://registry.npmmirror.com/prettier/-/prettier-2.8.8.tgz#e8c5d7e98a4305ffe3de2e1fc4aca1a71c28b1da"
resolved "https://registry.npmmirror.com/prettier/-/prettier-2.8.8.tgz"
integrity sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==
protobufjs@^7.5.4:
version "7.5.4"
resolved "https://registry.npmmirror.com/protobufjs/-/protobufjs-7.5.4.tgz#885d31fe9c4b37f25d1bb600da30b1c5b37d286a"
resolved "https://registry.npmmirror.com/protobufjs/-/protobufjs-7.5.4.tgz"
integrity sha512-CvexbZtbov6jW2eXAvLukXjXUW1TzFaivC46BpWc/3BpcCysb5Vffu+B3XHMm8lVEuy2Mm4XGex8hBSg1yapPg==
dependencies:
"@protobufjs/aspromise" "^1.1.2"
@ -568,17 +463,17 @@ protobufjs@^7.5.4:
proxy-from-env@^1.1.0:
version "1.1.0"
resolved "https://registry.npmmirror.com/proxy-from-env/-/proxy-from-env-1.1.0.tgz#e102f16ca355424865755d2c9e8ea4f24d58c3e2"
resolved "https://registry.npmmirror.com/proxy-from-env/-/proxy-from-env-1.1.0.tgz"
integrity sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==
regenerator-runtime@^0.13.10:
version "0.13.11"
resolved "https://registry.npmmirror.com/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz#f6dca3e7ceec20590d07ada785636a90cdca17f9"
resolved "https://registry.npmmirror.com/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz"
integrity sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==
resolve@^1.22.1:
version "1.22.10"
resolved "https://registry.npmmirror.com/resolve/-/resolve-1.22.10.tgz#b663e83ffb09bbf2386944736baae803029b8b39"
resolved "https://registry.npmmirror.com/resolve/-/resolve-1.22.10.tgz"
integrity sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==
dependencies:
is-core-module "^2.16.0"
@ -587,19 +482,19 @@ resolve@^1.22.1:
rollup@^2.79.1:
version "2.79.2"
resolved "https://registry.npmmirror.com/rollup/-/rollup-2.79.2.tgz#f150e4a5db4b121a21a747d762f701e5e9f49090"
resolved "https://registry.npmmirror.com/rollup/-/rollup-2.79.2.tgz"
integrity sha512-fS6iqSPZDs3dr/y7Od6y5nha8dW1YnbgtsyotCVvoFGKbERG++CVRFv1meyGDE1SNItQA8BrnCw7ScdAhRJ3XQ==
optionalDependencies:
fsevents "~2.3.2"
source-map-js@^1.2.1:
version "1.2.1"
resolved "https://registry.npmmirror.com/source-map-js/-/source-map-js-1.2.1.tgz#1ce5650fddd87abc099eda37dcff024c2667ae46"
resolved "https://registry.npmmirror.com/source-map-js/-/source-map-js-1.2.1.tgz"
integrity sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==
source-map-support@~0.5.20:
version "0.5.21"
resolved "https://registry.npmmirror.com/source-map-support/-/source-map-support-0.5.21.tgz#04fe7c7f9e1ed2d662233c28cb2b35b9f63f6e4f"
resolved "https://registry.npmmirror.com/source-map-support/-/source-map-support-0.5.21.tgz"
integrity sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==
dependencies:
buffer-from "^1.0.0"
@ -607,27 +502,32 @@ source-map-support@~0.5.20:
source-map@^0.6.0, source-map@^0.6.1:
version "0.6.1"
resolved "https://registry.npmmirror.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263"
resolved "https://registry.npmmirror.com/source-map/-/source-map-0.6.1.tgz"
integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==
sourcemap-codec@^1.4.8:
version "1.4.8"
resolved "https://registry.npmmirror.com/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz#ea804bd94857402e6992d05a38ef1ae35a9ab4c4"
resolved "https://registry.npmmirror.com/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz"
integrity sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==
store@^2.0.12:
version "2.0.12"
resolved "https://registry.npmmirror.com/store/-/store-2.0.12.tgz"
integrity sha512-eO9xlzDpXLiMr9W1nQ3Nfp9EzZieIQc10zPPMP5jsVV7bLOziSFFBP0XoDXACEIFtdI+rIz0NwWVA/QVJ8zJtw==
supports-preserve-symlinks-flag@^1.0.0:
version "1.0.0"
resolved "https://registry.npmmirror.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09"
resolved "https://registry.npmmirror.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz"
integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==
systemjs@^6.13.0:
version "6.15.1"
resolved "https://registry.npmmirror.com/systemjs/-/systemjs-6.15.1.tgz#74175b6810e27a79e1177d21db5f0e3057118cea"
resolved "https://registry.npmmirror.com/systemjs/-/systemjs-6.15.1.tgz"
integrity sha512-Nk8c4lXvMB98MtbmjX7JwJRgJOL8fluecYCfCeYBznwmpOs8Bf15hLM6z4z71EDAhQVrQrI+wt1aLWSXZq+hXA==
terser@^5.14.2:
terser@^5.14.2, terser@^5.4.0:
version "5.43.1"
resolved "https://registry.npmmirror.com/terser/-/terser-5.43.1.tgz#88387f4f9794ff1a29e7ad61fb2932e25b4fdb6d"
resolved "https://registry.npmmirror.com/terser/-/terser-5.43.1.tgz"
integrity sha512-+6erLbBm0+LROX2sPXlUYx/ux5PyE9K/a92Wrt6oA+WDAoFTdpHE5tCYCI5PNzq2y8df4rA+QgHLJuR4jNymsg==
dependencies:
"@jridgewell/source-map" "^0.3.3"
@ -637,12 +537,12 @@ terser@^5.14.2:
undici-types@~7.10.0:
version "7.10.0"
resolved "https://registry.npmmirror.com/undici-types/-/undici-types-7.10.0.tgz#4ac2e058ce56b462b056e629cc6a02393d3ff350"
resolved "https://registry.npmmirror.com/undici-types/-/undici-types-7.10.0.tgz"
integrity sha512-t5Fy/nfn+14LuOc2KNYg75vZqClpAiqscVvMygNnlsHBFpSXdJaYtXMcdNLpl/Qvc3P2cB3s6lOV51nqsFq4ag==
vite@^3.0.2:
vite@^3.0.0, vite@^3.0.2, vite@>=2.5.10:
version "3.2.11"
resolved "https://registry.npmmirror.com/vite/-/vite-3.2.11.tgz#8d1c8e05ef2f24b04c8693f56d3e01fe8835e6d7"
resolved "https://registry.npmmirror.com/vite/-/vite-3.2.11.tgz"
integrity sha512-K/jGKL/PgbIgKCiJo5QbASQhFiV02X9Jh+Qq0AKCRCRKZtOTVi4t6wh75FDpGf2N9rYOnzH87OEFQNaFy6pdxQ==
dependencies:
esbuild "^0.15.9"
@ -654,18 +554,23 @@ vite@^3.0.2:
vue-demi@^0.14.10:
version "0.14.10"
resolved "https://registry.npmmirror.com/vue-demi/-/vue-demi-0.14.10.tgz#afc78de3d6f9e11bf78c55e8510ee12814522f04"
resolved "https://registry.npmmirror.com/vue-demi/-/vue-demi-0.14.10.tgz"
integrity sha512-nMZBOwuzabUO0nLgIcc6rycZEebF6eeUfaiQx9+WSk8e29IbLvPU9feI6tqW4kTo3hvoYAJkMh8n8D0fuISphg==
vue-router@^3.5.4:
version "3.6.5"
resolved "https://registry.npmmirror.com/vue-router/-/vue-router-3.6.5.tgz#95847d52b9a7e3f1361cb605c8e6441f202afad8"
resolved "https://registry.npmmirror.com/vue-router/-/vue-router-3.6.5.tgz"
integrity sha512-VYXZQLtjuvKxxcshuRAwjHnciqZVoXAjTjcqBTz4rKc8qih9g9pI3hbDjmqXaHdgL3v8pV6P8Z335XvHzESxLQ==
vue@^2.7.7:
vue@^2.0.0, "vue@^2.7.0 || ^3.5.11", vue@^2.7.0-0, vue@^2.7.7, "vue@^3.0.0-0 || ^2.6.0":
version "2.7.16"
resolved "https://registry.npmmirror.com/vue/-/vue-2.7.16.tgz#98c60de9def99c0e3da8dae59b304ead43b967c9"
resolved "https://registry.npmmirror.com/vue/-/vue-2.7.16.tgz"
integrity sha512-4gCtFXaAA3zYZdTp5s4Hl2sozuySsgz4jy1EnpBHNfpMa9dK1ZCG7viqBPCwXtmgc8nHqUsAu3G4gtmXkkY3Sw==
dependencies:
"@vue/compiler-sfc" "2.7.16"
csstype "^3.1.0"
vuex@^3.1.1:
version "3.6.2"
resolved "https://registry.npmmirror.com/vuex/-/vuex-3.6.2.tgz"
integrity sha512-ETW44IqCgBpVomy520DT5jf8n0zoCac+sxWnn+hMe/CzaSejb/eVw2YToiXYX+Ex/AuHHia28vWTq4goAexFbw==

Loading…
Cancel
Save