算了,后端我自己写吧
This commit is contained in:
@@ -2,6 +2,15 @@
|
|||||||
"version": 2,
|
"version": 2,
|
||||||
"sessions": {
|
"sessions": {
|
||||||
"c9d5673cc5a442d4afa0a2e7805acb2d": [
|
"c9d5673cc5a442d4afa0a2e7805acb2d": [
|
||||||
|
{
|
||||||
|
"expertId": "BackendArchitect",
|
||||||
|
"name": "Joy",
|
||||||
|
"profession": "后端架构师",
|
||||||
|
"avatarUrl": "https://acc-1258344699.cos.accelerate.myqcloud.com/workbuddy/experts/avatars/02-Engineering/BackendArchitect/BackendArchitect.png",
|
||||||
|
"promptUrl": "https://acc-1258344699.cos.accelerate.myqcloud.com/workbuddy/experts/experts/02-Engineering/BackendArchitect/BackendArchitect_zh.md",
|
||||||
|
"usedAt": 1775015316468,
|
||||||
|
"industryId": "all"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"expertId": "SeniorDeveloper",
|
"expertId": "SeniorDeveloper",
|
||||||
"name": "Will",
|
"name": "Will",
|
||||||
@@ -13,5 +22,5 @@
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"lastUpdated": 1774965668803
|
"lastUpdated": 1775015525304
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
# 2026-04-01 工作日志
|
||||||
|
|
||||||
|
## 修复 SQLite CGO 启动报错 ✅ (11:20)
|
||||||
|
|
||||||
|
- **问题**:fresh 启动时报 `CGO_ENABLED=0` 导致 go-sqlite3 无法工作
|
||||||
|
- **原因**:fresh 不是通过 run-dev.bat 启动,没有继承 `CGO_ENABLED=1` 环境变量
|
||||||
|
- **修复**:
|
||||||
|
- 更新 `run-dev.bat`:改为用 fresh 启动,并确保 `set CGO_ENABLED=1` 在 fresh 之前执行
|
||||||
|
- 更新 `start-dev.bat`:同样加上 `set CGO_ENABLED=1`
|
||||||
|
- 创建 `runner.conf`(fresh 配置文件)
|
||||||
|
- **正确启动方式**:在 backend/ 目录执行 `.\run-dev.bat`,或 PowerShell 中设置 `$env:CGO_ENABLED="1"` 后 `go run .`
|
||||||
|
- **GCC 问题**:已安装 TDM-GCC v10.3.0
|
||||||
|
- **Fresh 问题**:runner-build.exe 缓存损坏,已清理并改用 `go run .` 启动
|
||||||
|
|
||||||
|
## 后端入口迁移:cmd/ops-server/main.go → 根目录 main.go ✅ (11:05)
|
||||||
|
|
||||||
|
- 将新架构 `cmd/ops-server/main.go` 内容合并到根目录 `backend/main.go`
|
||||||
|
- 删除 `cmd/` 目录
|
||||||
|
- 更新 `run-dev.bat` 和 `start-dev.bat` 启动命令从 `go run ./cmd/ops-server/main.go` 改为 `go run .`
|
||||||
|
- 编译验证通过(0 errors)
|
||||||
|
- 现在直接在 `backend/` 目录下运行 `go run .` 即可启动
|
||||||
@@ -93,7 +93,7 @@
|
|||||||
- **统一API响应**:标准错误码映射和响应格式
|
- **统一API响应**:标准错误码映射和响应格式
|
||||||
- **模块化路由系统**:API v1 版本路由定义清晰分离
|
- **模块化路由系统**:API v1 版本路由定义清晰分离
|
||||||
- **新目录结构**:
|
- **新目录结构**:
|
||||||
- `cmd/ops-server/main.go` - 应用入口
|
- `main.go` - 应用入口(已从 cmd/ops-server/main.go 合并至根目录,2026-04-01)
|
||||||
- `internal/config/` - 配置管理
|
- `internal/config/` - 配置管理
|
||||||
- `internal/database/` - 数据库连接和迁移
|
- `internal/database/` - 数据库连接和迁移
|
||||||
- `internal/handler/` - HTTP处理器(auth_handler.go, purchase_handler.go)
|
- `internal/handler/` - HTTP处理器(auth_handler.go, purchase_handler.go)
|
||||||
@@ -112,7 +112,7 @@
|
|||||||
- ✅ **编译状态**:项目编译成功(需要CGO_ENABLED=1以支持SQLite)
|
- ✅ **编译状态**:项目编译成功(需要CGO_ENABLED=1以支持SQLite)
|
||||||
|
|
||||||
### 新路由架构(2026-03-31)
|
### 新路由架构(2026-03-31)
|
||||||
- **主入口**:`cmd/ops-server/main.go` - 现代化主入口,支持优雅关机
|
- **主入口**:`main.go`(根目录)- 现代化主入口,支持优雅关机
|
||||||
- **路由配置**:`api/`包统一管理所有路由
|
- **路由配置**:`api/`包统一管理所有路由
|
||||||
- **兼容性**:完全兼容现有前端API `/api/*`
|
- **兼容性**:完全兼容现有前端API `/api/*`
|
||||||
- **新增API**:RESTful API v1 `/api/v1/*`
|
- **新增API**:RESTful API v1 `/api/v1/*`
|
||||||
|
|||||||
@@ -38,27 +38,6 @@ func RegisterRoutes(r *gin.RouterGroup) {
|
|||||||
// 静态文件路由 - 保持兼容性
|
// 静态文件路由 - 保持兼容性
|
||||||
r.StaticFS("/static", http.Dir("./dist"))
|
r.StaticFS("/static", http.Dir("./dist"))
|
||||||
|
|
||||||
// 兼容旧版文件路由(保持前端兼容性)
|
|
||||||
r.GET("/files/:mode/:hash", func(c *gin.Context) {
|
|
||||||
mode := c.Param("mode")
|
|
||||||
|
|
||||||
if mode == "get" || mode == "download" {
|
|
||||||
download := (mode == "download")
|
|
||||||
// 直接调用handler的GetFile/DownloadFile方法
|
|
||||||
if download {
|
|
||||||
fileHandler.DownloadFile(c)
|
|
||||||
} else {
|
|
||||||
fileHandler.GetFile(c)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
c.JSON(http.StatusBadRequest, gin.H{
|
|
||||||
"code": "-2",
|
|
||||||
"message": "无效的文件模式",
|
|
||||||
"data": nil,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
// 用户认证相关路由
|
// 用户认证相关路由
|
||||||
userGroup := r.Group("/users")
|
userGroup := r.Group("/users")
|
||||||
{
|
{
|
||||||
@@ -81,20 +60,13 @@ func RegisterRoutes(r *gin.RouterGroup) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 文件上传管理 - v1 API
|
// 文件上传管理 - v1 API
|
||||||
fileGroup := r.Group("/files")
|
// 注意:具体路由必须放在通配路由之前
|
||||||
{
|
r.POST("/files/upload", middleware.AuthToken(), fileHandler.UploadFile)
|
||||||
// 上传文件
|
r.GET("/files/list", middleware.AuthToken(), fileHandler.GetFileList)
|
||||||
fileGroup.POST("/upload", middleware.AuthToken(), fileHandler.UploadFile)
|
r.GET("/files/:id", middleware.AuthToken(), fileHandler.GetFileByID)
|
||||||
|
r.DELETE("/files/:id", middleware.AuthToken(), fileHandler.DeleteFile)
|
||||||
// 文件列表管理
|
r.GET("/files/download/:hash", fileHandler.DownloadFile)
|
||||||
fileGroup.GET("/list", middleware.AuthToken(), fileHandler.GetFileList)
|
r.GET("/files/get/:hash", fileHandler.GetFile)
|
||||||
fileGroup.GET("/:id", middleware.AuthToken(), fileHandler.GetFileByID)
|
|
||||||
fileGroup.DELETE("/:id", middleware.AuthToken(), fileHandler.DeleteFile)
|
|
||||||
|
|
||||||
// 文件访问
|
|
||||||
fileGroup.GET("/download/:hash", fileHandler.DownloadFile)
|
|
||||||
fileGroup.GET("/get/:hash", fileHandler.GetFile)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 采购订单管理
|
// 采购订单管理
|
||||||
purchaseGroup := r.Group("/purchase")
|
purchaseGroup := r.Group("/purchase")
|
||||||
@@ -51,36 +51,45 @@ func main() {
|
|||||||
gin.SetMode(gin.ReleaseMode)
|
gin.SetMode(gin.ReleaseMode)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 创建Gin实例(使用自定义logger)
|
// 创建Gin实例
|
||||||
r := gin.New()
|
r := gin.New()
|
||||||
|
|
||||||
// 注册中间件
|
// 注册中间件
|
||||||
r.Use(middleware.CORS())
|
r.Use(middleware.CORS())
|
||||||
|
|
||||||
// 根据环境选择日志中间件
|
// 根据环境选择日志中间件
|
||||||
if config.Current.Web.Host == "127.0.0.1" || config.Current.Web.Host == "localhost" {
|
if config.Current.Web.Host == "127.0.0.1" || config.Current.Web.Host == "localhost" {
|
||||||
// 开发环境使用简易日志
|
|
||||||
r.Use(middleware.SimpleLogger())
|
r.Use(middleware.SimpleLogger())
|
||||||
} else {
|
} else {
|
||||||
// 生产环境使用高级日志
|
|
||||||
r.Use(middleware.Logger(logger))
|
r.Use(middleware.Logger(logger))
|
||||||
}
|
}
|
||||||
|
|
||||||
r.Use(middleware.Recovery(logger))
|
r.Use(middleware.Recovery(logger))
|
||||||
|
|
||||||
// 注册API路由
|
// 注册API路由
|
||||||
registerRoutes(r, logger)
|
api.RegisterAllRoutes(r)
|
||||||
|
|
||||||
// 静态文件服务中间件(由api.CreateRouter处理)
|
// 健康检查端点
|
||||||
// 这里仅创建dist目录(如果不存在)
|
r.GET("/health", func(c *gin.Context) {
|
||||||
|
c.JSON(200, gin.H{
|
||||||
|
"code": "0",
|
||||||
|
"message": "Server is healthy",
|
||||||
|
"data": gin.H{
|
||||||
|
"timestamp": time.Now().Unix(),
|
||||||
|
"status": "running",
|
||||||
|
"version": "1.0.0",
|
||||||
|
},
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
// 确保dist目录存在
|
||||||
ensureDistDirectory(logger)
|
ensureDistDirectory(logger)
|
||||||
|
|
||||||
// 启动HTTP服务器
|
// 启动HTTP服务器
|
||||||
addr := fmt.Sprintf("%s:%s", config.Current.Web.Host, config.Current.Web.Port)
|
addr := fmt.Sprintf("%s:%s", config.Current.Web.Host, config.Current.Web.Port)
|
||||||
srv := &http.Server{
|
srv := &http.Server{
|
||||||
Addr: addr,
|
Addr: addr,
|
||||||
Handler: r,
|
Handler: r,
|
||||||
// 优化服务器配置
|
|
||||||
ReadTimeout: 15 * time.Second,
|
ReadTimeout: 15 * time.Second,
|
||||||
WriteTimeout: 15 * time.Second,
|
WriteTimeout: 15 * time.Second,
|
||||||
IdleTimeout: 60 * time.Second,
|
IdleTimeout: 60 * time.Second,
|
||||||
@@ -110,40 +119,11 @@ func main() {
|
|||||||
logger.Info("Server exited")
|
logger.Info("Server exited")
|
||||||
}
|
}
|
||||||
|
|
||||||
// 注册API路由
|
// createLogger 创建日志记录器
|
||||||
func registerRoutes(r *gin.Engine, logger *zap.Logger) {
|
|
||||||
// 使用我们的路由配置
|
|
||||||
api.RegisterAllRoutes(r)
|
|
||||||
|
|
||||||
// 健康检查端点(额外添加)
|
|
||||||
r.GET("/health", func(c *gin.Context) {
|
|
||||||
c.JSON(200, gin.H{
|
|
||||||
"code": "0",
|
|
||||||
"message": "Server is healthy",
|
|
||||||
"data": gin.H{
|
|
||||||
"timestamp": time.Now().Unix(),
|
|
||||||
"status": "running",
|
|
||||||
"version": "1.0.0",
|
|
||||||
},
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// 兼容性路由,保持原有API结构
|
|
||||||
// 注意:此函数现在由api包统一处理,此函数保留作为参考
|
|
||||||
func compatRoutes(api *gin.RouterGroup, logger *zap.Logger) {
|
|
||||||
logger.Info("兼容性路由由api包统一管理")
|
|
||||||
}
|
|
||||||
|
|
||||||
// 静态文件服务(已迁移到api包)
|
|
||||||
|
|
||||||
// 创建日志记录器
|
|
||||||
func createLogger() (*zap.Logger, error) {
|
func createLogger() (*zap.Logger, error) {
|
||||||
// 开发环境使用开发配置
|
|
||||||
if gin.Mode() == gin.DebugMode {
|
if gin.Mode() == gin.DebugMode {
|
||||||
return zap.NewDevelopment()
|
return zap.NewDevelopment()
|
||||||
}
|
}
|
||||||
// 生产环境使用生产配置
|
|
||||||
return zap.NewProduction()
|
return zap.NewProduction()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -157,4 +137,3 @@ func ensureDistDirectory(logger *zap.Logger) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
@echo off
|
||||||
|
REM OPS Backend 开发服务器启动脚本
|
||||||
|
REM 设置 CGO 和 GCC 路径
|
||||||
|
set CGO_ENABLED=1
|
||||||
|
set PATH=C:\TDM-GCC-64\bin;%PATH%
|
||||||
|
|
||||||
|
cd /d %~dp0
|
||||||
|
|
||||||
|
echo Starting OPS Backend (with auto-reload)...
|
||||||
|
echo.
|
||||||
|
|
||||||
|
REM 使用 gin 自动重载(比 fresh 更稳定)
|
||||||
|
go run . -port 8080
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
root: .
|
||||||
|
tmp_path: ./tmp
|
||||||
|
build_name: runner-build
|
||||||
|
build_log: runner-build-errors.log
|
||||||
|
valid_ext: .go, .tpl, .tmpl, .html
|
||||||
|
no_rebuild_ext: .tpl, .tmpl, .html
|
||||||
|
ignored: assets, tmp, vendor, frontend, dist
|
||||||
|
build_delay: 600
|
||||||
|
colors: 1
|
||||||
|
log_color_main: cyan
|
||||||
|
log_color_build: yellow
|
||||||
|
log_color_runner: green
|
||||||
|
log_color_watcher: magenta
|
||||||
|
log_color_app:
|
||||||
@@ -8,9 +8,12 @@ if not exist "./dist" (
|
|||||||
echo.
|
echo.
|
||||||
)
|
)
|
||||||
|
|
||||||
|
REM SQLite 需要 CGO
|
||||||
|
set CGO_ENABLED=1
|
||||||
|
|
||||||
REM 运行新的重构版本
|
REM 运行新的重构版本
|
||||||
echo Running new refactored backend...
|
echo Running new refactored backend...
|
||||||
echo.
|
echo.
|
||||||
go run ./cmd/ops-server/main.go
|
go run .
|
||||||
|
|
||||||
pause
|
pause
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
# Binaries for programs and plugins
|
||||||
|
*.exe
|
||||||
|
*.exe~
|
||||||
|
*.dll
|
||||||
|
*.so
|
||||||
|
*.dylib
|
||||||
|
|
||||||
|
.DS_Store
|
||||||
|
/dist
|
||||||
|
/data
|
||||||
|
/tmp
|
||||||
|
|
||||||
|
/build
|
||||||
|
|
||||||
|
/test
|
||||||
|
|
||||||
|
*.db
|
||||||
|
|
||||||
|
OPSYS
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
APP_NAME="OPSYS"
|
||||||
|
APP_PATH="./build/$APP_NAME"
|
||||||
|
|
||||||
|
# 编译应用
|
||||||
|
echo "编译应用..."
|
||||||
|
CGO_ENABLED=0 GOOS=linux go build -o $APP_NAME -ldflags="-s -w" main.go
|
||||||
|
|
||||||
|
# 创建目录
|
||||||
|
echo "创建目录..."
|
||||||
|
sudo mkdir -p $APP_PATH
|
||||||
|
|
||||||
|
# 复制文件
|
||||||
|
echo "复制文件..."
|
||||||
|
sudo cp $APP_NAME $APP_PATH/
|
||||||
|
sudo cp -r defConfig $APP_PATH/
|
||||||
|
sudo cp -r dist $APP_PATH/
|
||||||
@@ -0,0 +1,57 @@
|
|||||||
|
version: "0.3.1"
|
||||||
|
|
||||||
|
web:
|
||||||
|
host: "127.0.0.1"
|
||||||
|
port: "8080"
|
||||||
|
tls: false
|
||||||
|
certPrivatePath: ""
|
||||||
|
certPublicPath: ""
|
||||||
|
|
||||||
|
database:
|
||||||
|
type: "sqlite" # mysql pg or sqlite
|
||||||
|
path: "data/database.db" # sqlite path
|
||||||
|
host: "" # mysql host
|
||||||
|
port: ""
|
||||||
|
name: ""
|
||||||
|
user: ""
|
||||||
|
pass: ""
|
||||||
|
|
||||||
|
user:
|
||||||
|
cookieTimeout: 604800
|
||||||
|
passHashType: "md5" #密码哈希类型 text md5 md5salt
|
||||||
|
|
||||||
|
|
||||||
|
file:
|
||||||
|
maxSize: 52428800
|
||||||
|
pahts:
|
||||||
|
avatar: "data/static/avatar/"
|
||||||
|
image: "data/upload/image/"
|
||||||
|
video: "data/upload/video/"
|
||||||
|
music: "data/upload/music/"
|
||||||
|
pdf: "data/upload/pdf/"
|
||||||
|
other: "data/upload/other/"
|
||||||
|
|
||||||
|
allowImageMime:
|
||||||
|
image/jpeg: ".jpeg"
|
||||||
|
image/png: ".png"
|
||||||
|
image/gif: ".gif"
|
||||||
|
image/bmp: ".bmp"
|
||||||
|
|
||||||
|
allowVideoMime:
|
||||||
|
video/mp4: ".mp4"
|
||||||
|
video/x-msvideo: ".avi"
|
||||||
|
video/quicktime: ".mov"
|
||||||
|
video/x-flv: ".flv"
|
||||||
|
video/mpeg: ".mpeg"
|
||||||
|
|
||||||
|
allowMusicMime:
|
||||||
|
audio/mpeg: ".mpeg"
|
||||||
|
audio/aac: ".aac"
|
||||||
|
audio/wav: ".wav"
|
||||||
|
audio/flac: ".flac"
|
||||||
|
|
||||||
|
allowPdfMime:
|
||||||
|
application/pdf: ".pdf"
|
||||||
|
|
||||||
|
|
||||||
|
configed: false
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
{
|
||||||
|
"apiOK":0,
|
||||||
|
"apiErr":-1,
|
||||||
|
"postErr":-2,
|
||||||
|
"jsonErr":-3,
|
||||||
|
"userNameDup":-4,
|
||||||
|
"userNameNoFund":-41,
|
||||||
|
"userPassIncorrect":-42,
|
||||||
|
"userEmailFormatError":-43,
|
||||||
|
"userCookieError":-44,
|
||||||
|
"userCookieNotFund":-44,
|
||||||
|
"userCookieExpired":-44,
|
||||||
|
"file_mime_err":-51,
|
||||||
|
"file_size_err":-52,
|
||||||
|
"file_name_err":-53,
|
||||||
|
"file_get_err":-54,
|
||||||
|
"file_hash_err":-55,
|
||||||
|
"file_save_err":-56,
|
||||||
|
"file_not_found":-57,
|
||||||
|
"file_part_err":-58
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
export PATH=$PATH:$(go env GOPATH)/bin
|
||||||
|
|
||||||
|
fresh
|
||||||
|
|
||||||
@@ -0,0 +1,62 @@
|
|||||||
|
module ops
|
||||||
|
|
||||||
|
go 1.24.0
|
||||||
|
|
||||||
|
toolchain go1.24.9
|
||||||
|
|
||||||
|
require (
|
||||||
|
filippo.io/edwards25519 v1.1.0 // indirect
|
||||||
|
github.com/bytedance/sonic v1.14.0 // indirect
|
||||||
|
github.com/bytedance/sonic/loader v0.3.0 // indirect
|
||||||
|
github.com/cloudwego/base64x v0.1.6 // indirect
|
||||||
|
github.com/dustin/go-humanize v1.0.1 // indirect
|
||||||
|
github.com/gabriel-vasile/mimetype v1.4.8 // indirect
|
||||||
|
github.com/gin-contrib/sse v1.1.0 // indirect
|
||||||
|
github.com/gin-gonic/gin v1.11.0 // indirect
|
||||||
|
github.com/glebarez/go-sqlite v1.21.2 // indirect
|
||||||
|
github.com/glebarez/sqlite v1.11.0 // indirect
|
||||||
|
github.com/go-playground/locales v0.14.1 // indirect
|
||||||
|
github.com/go-playground/universal-translator v0.18.1 // indirect
|
||||||
|
github.com/go-playground/validator/v10 v10.27.0 // indirect
|
||||||
|
github.com/go-sql-driver/mysql v1.9.3 // indirect
|
||||||
|
github.com/goccy/go-json v0.10.2 // indirect
|
||||||
|
github.com/goccy/go-yaml v1.18.0 // indirect
|
||||||
|
github.com/google/uuid v1.6.0 // indirect
|
||||||
|
github.com/jackc/pgpassfile v1.0.0 // indirect
|
||||||
|
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect
|
||||||
|
github.com/jackc/pgx/v5 v5.6.0 // indirect
|
||||||
|
github.com/jackc/puddle/v2 v2.2.2 // indirect
|
||||||
|
github.com/jinzhu/inflection v1.0.0 // indirect
|
||||||
|
github.com/jinzhu/now v1.1.5 // indirect
|
||||||
|
github.com/json-iterator/go v1.1.12 // indirect
|
||||||
|
github.com/klauspost/cpuid/v2 v2.3.0 // indirect
|
||||||
|
github.com/leodido/go-urn v1.4.0 // indirect
|
||||||
|
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||||
|
github.com/mitchellh/mapstructure v1.5.0 // indirect
|
||||||
|
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 // indirect
|
||||||
|
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||||
|
github.com/pelletier/go-toml/v2 v2.2.4 // indirect
|
||||||
|
github.com/quic-go/qpack v0.5.1 // indirect
|
||||||
|
github.com/quic-go/quic-go v0.54.0 // indirect
|
||||||
|
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
|
||||||
|
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
|
||||||
|
github.com/ugorji/go/codec v1.3.0 // indirect
|
||||||
|
go.uber.org/mock v0.5.0 // indirect
|
||||||
|
golang.org/x/arch v0.20.0 // indirect
|
||||||
|
golang.org/x/crypto v0.46.0 // indirect
|
||||||
|
golang.org/x/mod v0.31.0 // indirect
|
||||||
|
golang.org/x/net v0.48.0 // indirect
|
||||||
|
golang.org/x/sync v0.19.0 // indirect
|
||||||
|
golang.org/x/sys v0.39.0 // indirect
|
||||||
|
golang.org/x/text v0.33.0 // indirect
|
||||||
|
golang.org/x/tools v0.40.0 // indirect
|
||||||
|
google.golang.org/protobuf v1.36.9 // indirect
|
||||||
|
gorm.io/datatypes v1.2.7 // indirect
|
||||||
|
gorm.io/driver/mysql v1.6.0 // indirect
|
||||||
|
gorm.io/driver/postgres v1.6.0 // indirect
|
||||||
|
gorm.io/gorm v1.31.1 // indirect
|
||||||
|
modernc.org/libc v1.22.5 // indirect
|
||||||
|
modernc.org/mathutil v1.5.0 // indirect
|
||||||
|
modernc.org/memory v1.5.0 // indirect
|
||||||
|
modernc.org/sqlite v1.23.1 // indirect
|
||||||
|
)
|
||||||
@@ -0,0 +1,144 @@
|
|||||||
|
filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=
|
||||||
|
filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
|
||||||
|
github.com/bytedance/sonic v1.14.0 h1:/OfKt8HFw0kh2rj8N0F6C/qPGRESq0BbaNZgcNXXzQQ=
|
||||||
|
github.com/bytedance/sonic v1.14.0/go.mod h1:WoEbx8WTcFJfzCe0hbmyTGrfjt8PzNEBdxlNUO24NhA=
|
||||||
|
github.com/bytedance/sonic/loader v0.3.0 h1:dskwH8edlzNMctoruo8FPTJDF3vLtDT0sXZwvZJyqeA=
|
||||||
|
github.com/bytedance/sonic/loader v0.3.0/go.mod h1:N8A3vUdtUebEY2/VQC0MyhYeKUFosQU6FxH2JmUe6VI=
|
||||||
|
github.com/cloudwego/base64x v0.1.6 h1:t11wG9AECkCDk5fMSoxmufanudBtJ+/HemLstXDLI2M=
|
||||||
|
github.com/cloudwego/base64x v0.1.6/go.mod h1:OFcloc187FXDaYHvrNIjxSe8ncn0OOM8gEHfghB2IPU=
|
||||||
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
|
||||||
|
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
|
||||||
|
github.com/gabriel-vasile/mimetype v1.4.8 h1:FfZ3gj38NjllZIeJAmMhr+qKL8Wu+nOoI3GqacKw1NM=
|
||||||
|
github.com/gabriel-vasile/mimetype v1.4.8/go.mod h1:ByKUIKGjh1ODkGM1asKUbQZOLGrPjydw3hYPU2YU9t8=
|
||||||
|
github.com/gin-contrib/sse v1.1.0 h1:n0w2GMuUpWDVp7qSpvze6fAu9iRxJY4Hmj6AmBOU05w=
|
||||||
|
github.com/gin-contrib/sse v1.1.0/go.mod h1:hxRZ5gVpWMT7Z0B0gSNYqqsSCNIJMjzvm6fqCz9vjwM=
|
||||||
|
github.com/gin-gonic/gin v1.11.0 h1:OW/6PLjyusp2PPXtyxKHU0RbX6I/l28FTdDlae5ueWk=
|
||||||
|
github.com/gin-gonic/gin v1.11.0/go.mod h1:+iq/FyxlGzII0KHiBGjuNn4UNENUlKbGlNmc+W50Dls=
|
||||||
|
github.com/glebarez/go-sqlite v1.21.2 h1:3a6LFC4sKahUunAmynQKLZceZCOzUthkRkEAl9gAXWo=
|
||||||
|
github.com/glebarez/go-sqlite v1.21.2/go.mod h1:sfxdZyhQjTM2Wry3gVYWaW072Ri1WMdWJi0k6+3382k=
|
||||||
|
github.com/glebarez/sqlite v1.11.0 h1:wSG0irqzP6VurnMEpFGer5Li19RpIRi2qvQz++w0GMw=
|
||||||
|
github.com/glebarez/sqlite v1.11.0/go.mod h1:h8/o8j5wiAsqSPoWELDUdJXhjAhsVliSn7bWZjOhrgQ=
|
||||||
|
github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
|
||||||
|
github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
|
||||||
|
github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
|
||||||
|
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
|
||||||
|
github.com/go-playground/validator/v10 v10.27.0 h1:w8+XrWVMhGkxOaaowyKH35gFydVHOvC0/uWoy2Fzwn4=
|
||||||
|
github.com/go-playground/validator/v10 v10.27.0/go.mod h1:I5QpIEbmr8On7W0TktmJAumgzX4CA1XNl4ZmDuVHKKo=
|
||||||
|
github.com/go-sql-driver/mysql v1.8.1 h1:LedoTUt/eveggdHS9qUFC1EFSa8bU2+1pZjSRpvNJ1Y=
|
||||||
|
github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg=
|
||||||
|
github.com/go-sql-driver/mysql v1.9.3 h1:U/N249h2WzJ3Ukj8SowVFjdtZKfu9vlLZxjPXV1aweo=
|
||||||
|
github.com/go-sql-driver/mysql v1.9.3/go.mod h1:qn46aNg1333BRMNU69Lq93t8du/dwxI64Gl8i5p1WMU=
|
||||||
|
github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU=
|
||||||
|
github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
|
||||||
|
github.com/goccy/go-yaml v1.18.0 h1:8W7wMFS12Pcas7KU+VVkaiCng+kG8QiFeFwzFb+rwuw=
|
||||||
|
github.com/goccy/go-yaml v1.18.0/go.mod h1:XBurs7gK8ATbW4ZPGKgcbrY1Br56PdM69F7LkFRi1kA=
|
||||||
|
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||||
|
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
|
||||||
|
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
|
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||||
|
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
|
github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM=
|
||||||
|
github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg=
|
||||||
|
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 h1:iCEnooe7UlwOQYpKFhBabPMi4aNAfoODPEFNiAnClxo=
|
||||||
|
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM=
|
||||||
|
github.com/jackc/pgx/v5 v5.6.0 h1:SWJzexBzPL5jb0GEsrPMLIsi/3jOo7RHlzTjcAeDrPY=
|
||||||
|
github.com/jackc/pgx/v5 v5.6.0/go.mod h1:DNZ/vlrUnhWCoFGxHAG8U2ljioxukquj7utPDgtQdTw=
|
||||||
|
github.com/jackc/puddle/v2 v2.2.2 h1:PR8nw+E/1w0GLuRFSmiioY6UooMp6KJv0/61nB7icHo=
|
||||||
|
github.com/jackc/puddle/v2 v2.2.2/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4=
|
||||||
|
github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
|
||||||
|
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
|
||||||
|
github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ=
|
||||||
|
github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
|
||||||
|
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
|
||||||
|
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
|
||||||
|
github.com/klauspost/cpuid/v2 v2.3.0 h1:S4CRMLnYUhGeDFDqkGriYKdfoFlDnMtqTiI/sFzhA9Y=
|
||||||
|
github.com/klauspost/cpuid/v2 v2.3.0/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0=
|
||||||
|
github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ=
|
||||||
|
github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI=
|
||||||
|
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
||||||
|
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||||
|
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
|
||||||
|
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
||||||
|
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 h1:ZqeYNhU3OHLH3mGKHDcjJRFFRrJa6eAM5H+CtDdOsPc=
|
||||||
|
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||||
|
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
|
||||||
|
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
||||||
|
github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4=
|
||||||
|
github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY=
|
||||||
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
|
github.com/quic-go/qpack v0.5.1 h1:giqksBPnT/HDtZ6VhtFKgoLOWmlyo9Ei6u9PqzIMbhI=
|
||||||
|
github.com/quic-go/qpack v0.5.1/go.mod h1:+PC4XFrEskIVkcLzpEkbLqq1uCoxPhQuvK5rH1ZgaEg=
|
||||||
|
github.com/quic-go/quic-go v0.54.0 h1:6s1YB9QotYI6Ospeiguknbp2Znb/jZYjZLRXn9kMQBg=
|
||||||
|
github.com/quic-go/quic-go v0.54.0/go.mod h1:e68ZEaCdyviluZmy44P6Iey98v/Wfz6HCjQEm+l8zTY=
|
||||||
|
github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
|
||||||
|
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE=
|
||||||
|
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
|
||||||
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
|
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||||
|
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||||
|
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||||
|
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
|
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
|
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||||
|
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||||
|
github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI=
|
||||||
|
github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
|
||||||
|
github.com/ugorji/go/codec v1.3.0 h1:Qd2W2sQawAfG8XSvzwhBeoGq71zXOC/Q1E9y/wUcsUA=
|
||||||
|
github.com/ugorji/go/codec v1.3.0/go.mod h1:pRBVtBSKl77K30Bv8R2P+cLSGaTtex6fsA2Wjqmfxj4=
|
||||||
|
go.uber.org/mock v0.5.0 h1:KAMbZvZPyBPWgD14IrIQ38QCyjwpvVVV6K/bHl1IwQU=
|
||||||
|
go.uber.org/mock v0.5.0/go.mod h1:ge71pBPLYDk7QIi1LupWxdAykm7KIEFchiOqd6z7qMM=
|
||||||
|
golang.org/x/arch v0.20.0 h1:dx1zTU0MAE98U+TQ8BLl7XsJbgze2WnNKF/8tGp/Q6c=
|
||||||
|
golang.org/x/arch v0.20.0/go.mod h1:bdwinDaKcfZUGpH09BB7ZmOfhalA8lQdzl62l8gGWsk=
|
||||||
|
golang.org/x/crypto v0.40.0 h1:r4x+VvoG5Fm+eJcxMaY8CQM7Lb0l1lsmjGBQ6s8BfKM=
|
||||||
|
golang.org/x/crypto v0.40.0/go.mod h1:Qr1vMER5WyS2dfPHAlsOj01wgLbsyWtFn/aY+5+ZdxY=
|
||||||
|
golang.org/x/crypto v0.46.0 h1:cKRW/pmt1pKAfetfu+RCEvjvZkA9RimPbh7bhFjGVBU=
|
||||||
|
golang.org/x/crypto v0.46.0/go.mod h1:Evb/oLKmMraqjZ2iQTwDwvCtJkczlDuTmdJXoZVzqU0=
|
||||||
|
golang.org/x/mod v0.25.0 h1:n7a+ZbQKQA/Ysbyb0/6IbB1H/X41mKgbhfv7AfG/44w=
|
||||||
|
golang.org/x/mod v0.25.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww=
|
||||||
|
golang.org/x/mod v0.31.0 h1:HaW9xtz0+kOcWKwli0ZXy79Ix+UW/vOfmWI5QVd2tgI=
|
||||||
|
golang.org/x/mod v0.31.0/go.mod h1:43JraMp9cGx1Rx3AqioxrbrhNsLl2l/iNAvuBkrezpg=
|
||||||
|
golang.org/x/net v0.42.0 h1:jzkYrhi3YQWD6MLBJcsklgQsoAcw89EcZbJw8Z614hs=
|
||||||
|
golang.org/x/net v0.42.0/go.mod h1:FF1RA5d3u7nAYA4z2TkclSCKh68eSXtiFwcWQpPXdt8=
|
||||||
|
golang.org/x/net v0.48.0 h1:zyQRTTrjc33Lhh0fBgT/H3oZq9WuvRR5gPC70xpDiQU=
|
||||||
|
golang.org/x/net v0.48.0/go.mod h1:+ndRgGjkh8FGtu1w1FGbEC31if4VrNVMuKTgcAAnQRY=
|
||||||
|
golang.org/x/sync v0.16.0 h1:ycBJEhp9p4vXvUZNszeOq0kGTPghopOL8q0fq3vstxw=
|
||||||
|
golang.org/x/sync v0.16.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
|
||||||
|
golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4=
|
||||||
|
golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
|
||||||
|
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.35.0 h1:vz1N37gP5bs89s7He8XuIYXpyY0+QlsKmzipCbUtyxI=
|
||||||
|
golang.org/x/sys v0.35.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
||||||
|
golang.org/x/sys v0.39.0 h1:CvCKL8MeisomCi6qNZ+wbb0DN9E5AATixKsvNtMoMFk=
|
||||||
|
golang.org/x/sys v0.39.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
||||||
|
golang.org/x/text v0.27.0 h1:4fGWRpyh641NLlecmyl4LOe6yDdfaYNrGb2zdfo4JV4=
|
||||||
|
golang.org/x/text v0.27.0/go.mod h1:1D28KMCvyooCX9hBiosv5Tz/+YLxj0j7XhWjpSUF7CU=
|
||||||
|
golang.org/x/text v0.33.0 h1:B3njUFyqtHDUI5jMn1YIr5B0IE2U0qck04r6d4KPAxE=
|
||||||
|
golang.org/x/text v0.33.0/go.mod h1:LuMebE6+rBincTi9+xWTY8TztLzKHc/9C1uBCG27+q8=
|
||||||
|
golang.org/x/tools v0.34.0 h1:qIpSLOxeCYGg9TrcJokLBG4KFA6d795g0xkBkiESGlo=
|
||||||
|
golang.org/x/tools v0.34.0/go.mod h1:pAP9OwEaY1CAW3HOmg3hLZC5Z0CCmzjAF2UQMSqNARg=
|
||||||
|
golang.org/x/tools v0.40.0/go.mod h1:Ik/tzLRlbscWpqqMRjyWYDisX8bG13FrdXp3o4Sr9lc=
|
||||||
|
google.golang.org/protobuf v1.36.9 h1:w2gp2mA27hUeUzj9Ex9FBjsBm40zfaDtEWow293U7Iw=
|
||||||
|
google.golang.org/protobuf v1.36.9/go.mod h1:fuxRtAxBytpl4zzqUh6/eyUujkJdNiuEkXntxiD/uRU=
|
||||||
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
|
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
|
gorm.io/datatypes v1.2.7 h1:ww9GAhF1aGXZY3EB3cJPJ7//JiuQo7DlQA7NNlVaTdk=
|
||||||
|
gorm.io/datatypes v1.2.7/go.mod h1:M2iO+6S3hhi4nAyYe444Pcb0dcIiOMJ7QHaUXxyiNZY=
|
||||||
|
gorm.io/driver/mysql v1.6.0 h1:eNbLmNTpPpTOVZi8MMxCi2aaIm0ZpInbORNXDwyLGvg=
|
||||||
|
gorm.io/driver/mysql v1.6.0/go.mod h1:D/oCC2GWK3M/dqoLxnOlaNKmXz8WNTfcS9y5ovaSqKo=
|
||||||
|
gorm.io/driver/postgres v1.6.0 h1:2dxzU8xJ+ivvqTRph34QX+WrRaJlmfyPqXmoGVjMBa4=
|
||||||
|
gorm.io/driver/postgres v1.6.0/go.mod h1:vUw0mrGgrTK+uPHEhAdV4sfFELrByKVGnaVRkXDhtWo=
|
||||||
|
gorm.io/gorm v1.31.0 h1:0VlycGreVhK7RF/Bwt51Fk8v0xLiiiFdbGDPIZQ7mJY=
|
||||||
|
gorm.io/gorm v1.31.0/go.mod h1:XyQVbO2k6YkOis7C2437jSit3SsDK72s7n7rsSHd+Gs=
|
||||||
|
gorm.io/gorm v1.31.1 h1:7CA8FTFz/gRfgqgpeKIBcervUn3xSyPUmr6B2WXJ7kg=
|
||||||
|
gorm.io/gorm v1.31.1/go.mod h1:XyQVbO2k6YkOis7C2437jSit3SsDK72s7n7rsSHd+Gs=
|
||||||
|
modernc.org/libc v1.22.5 h1:91BNch/e5B0uPbJFgqbxXuOnxBQjlS//icfQEGmvyjE=
|
||||||
|
modernc.org/libc v1.22.5/go.mod h1:jj+Z7dTNX8fBScMVNRAYZ/jF91K8fdT2hYMThc3YjBY=
|
||||||
|
modernc.org/mathutil v1.5.0 h1:rV0Ko/6SfM+8G+yKiyI830l3Wuz1zRutdslNoQ0kfiQ=
|
||||||
|
modernc.org/mathutil v1.5.0/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E=
|
||||||
|
modernc.org/memory v1.5.0 h1:N+/8c5rE6EqugZwHii4IFsaJ7MUhoWX07J5tC/iI5Ds=
|
||||||
|
modernc.org/memory v1.5.0/go.mod h1:PkUhL0Mugw21sHPeskwZW4D6VscE/GQJOnIpCnW6pSU=
|
||||||
|
modernc.org/sqlite v1.23.1 h1:nrSBg4aRQQwq59JpvGEQ15tNxoO5pX/kUjcRNwSAGQM=
|
||||||
|
modernc.org/sqlite v1.23.1/go.mod h1:OrDj17Mggn6MhE+iPbBNf7RGKODDE9NFT0f3EwDzJqk=
|
||||||
@@ -0,0 +1,62 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
APP_NAME="OPSYS"
|
||||||
|
APP_PATH="/opt/$APP_NAME"
|
||||||
|
SERVICE_FILE="/etc/systemd/system/$APP_NAME.service"
|
||||||
|
LOG_PATH="/var/log/$APP_NAME"
|
||||||
|
|
||||||
|
echo "正在安装 $APP_NAME..."
|
||||||
|
|
||||||
|
# 编译应用
|
||||||
|
echo "编译应用..."
|
||||||
|
CGO_ENABLED=0 GOOS=linux go build -o $APP_NAME -ldflags="-s -w" main.go
|
||||||
|
|
||||||
|
# 创建目录
|
||||||
|
echo "创建目录..."
|
||||||
|
sudo mkdir -p $APP_PATH
|
||||||
|
sudo mkdir -p $LOG_PATH
|
||||||
|
|
||||||
|
# 复制文件
|
||||||
|
echo "复制文件..."
|
||||||
|
sudo cp $APP_NAME $APP_PATH/
|
||||||
|
sudo cp -r defConfig $APP_PATH/
|
||||||
|
sudo cp -r dist $APP_PATH/
|
||||||
|
|
||||||
|
# 创建服务文件
|
||||||
|
echo "创建服务文件..."
|
||||||
|
sudo tee $SERVICE_FILE > /dev/null <<EOF
|
||||||
|
[Unit]
|
||||||
|
Description=My Gin Application
|
||||||
|
After=network.target
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Type=simple
|
||||||
|
User=www-data
|
||||||
|
WorkingDirectory=$APP_PATH
|
||||||
|
ExecStart=$APP_PATH/$APP_NAME
|
||||||
|
Restart=always
|
||||||
|
RestartSec=5
|
||||||
|
StandardOutput=append:$LOG_PATH/access.log
|
||||||
|
StandardError=append:$LOG_PATH/error.log
|
||||||
|
Environment=GIN_MODE=release
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=multi-user.target
|
||||||
|
EOF
|
||||||
|
|
||||||
|
# 设置权限
|
||||||
|
echo "设置权限..."
|
||||||
|
sudo chown -R www-data:www-data $APP_PATH
|
||||||
|
sudo chown -R www-data:www-data $LOG_PATH
|
||||||
|
sudo chmod 750 $APP_PATH/$APP_NAME
|
||||||
|
|
||||||
|
# 重载并启动
|
||||||
|
echo "启动服务..."
|
||||||
|
sudo systemctl daemon-reload
|
||||||
|
sudo systemctl enable $APP_NAME
|
||||||
|
sudo systemctl start $APP_NAME
|
||||||
|
|
||||||
|
echo "安装完成!"
|
||||||
|
echo "使用以下命令管理服务:"
|
||||||
|
echo " sudo systemctl status $APP_NAME"
|
||||||
|
echo " sudo journalctl -u $APP_NAME -f"
|
||||||
@@ -0,0 +1,57 @@
|
|||||||
|
package models
|
||||||
|
|
||||||
|
import "github.com/mitchellh/mapstructure"
|
||||||
|
|
||||||
|
var Configs map[string]interface{}
|
||||||
|
|
||||||
|
type ConfigsWeb_ struct {
|
||||||
|
Host string `mapstructure:"host"`
|
||||||
|
Port string `mapstructure:"port"`
|
||||||
|
Tls bool `mapstructure:"tls"`
|
||||||
|
CertPrivatePath string `mapstructure:"certPrivatePath"`
|
||||||
|
CertPublicPath string `mapstructure:"certPublicPath"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ConfigsUser_ struct {
|
||||||
|
CookieTimeout int `mapstructure:"cookieTimeout"`
|
||||||
|
PassHashType string `mapstructure:"passHashType"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ConfigsFile_ struct {
|
||||||
|
MaxSize uint64 `mapstructure:"maxSize"`
|
||||||
|
Pahts map[string]string `mapstructure:"pahts"`
|
||||||
|
AllowImageMime map[string]string `mapstructure:"allowImageMime"`
|
||||||
|
AllowVideoMime map[string]string `mapstructure:"allowVideoMime"`
|
||||||
|
AllowMusicMime map[string]string `mapstructure:"allowMusicMime"`
|
||||||
|
AllowPdfMime map[string]string `mapstructure:"allowPdfMime"`
|
||||||
|
}
|
||||||
|
|
||||||
|
var ConfigsWed ConfigsWeb_
|
||||||
|
var ConfigsUser ConfigsUser_
|
||||||
|
var ConfigsFile ConfigsFile_
|
||||||
|
|
||||||
|
func ConfigAllInit() error {
|
||||||
|
|
||||||
|
//初始化数据库
|
||||||
|
DatabaseInit()
|
||||||
|
|
||||||
|
//读取web配置
|
||||||
|
err := mapstructure.Decode(Configs["web"].(map[string]interface{}), &ConfigsWed)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
//初始化user config
|
||||||
|
err = mapstructure.Decode(Configs["user"].(map[string]interface{}), &ConfigsUser)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
//初始化file config
|
||||||
|
err = mapstructure.Decode(Configs["file"].(map[string]interface{}), &ConfigsFile)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
@@ -0,0 +1,108 @@
|
|||||||
|
package models
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/md5"
|
||||||
|
"crypto/rand"
|
||||||
|
"encoding/hex"
|
||||||
|
"regexp"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// 获取当前时间字符串
|
||||||
|
// 参数格式可选,默认"2006-01-02 15:04:05"
|
||||||
|
func GetCurrentTimeString(format ...string) string {
|
||||||
|
// 默认格式
|
||||||
|
layout := "2006_01_02-15_04_05.999999999"
|
||||||
|
|
||||||
|
// 如果传入了格式参数则使用自定义格式
|
||||||
|
if len(format) > 0 {
|
||||||
|
layout = format[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
return time.Now().Format(layout)
|
||||||
|
}
|
||||||
|
|
||||||
|
func StringToTimePtr(str string) (*time.Time, error) {
|
||||||
|
layout := "2006-01-02 15:04"
|
||||||
|
t, err := time.Parse(layout, str)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &t, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func RandStr32() string {
|
||||||
|
// 生成 32 字节 (256 位) 随机数据
|
||||||
|
b := make([]byte, 32)
|
||||||
|
if _, err := rand.Read(b); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 转换为 16 进制字符串 (长度 64)
|
||||||
|
cookie := hex.EncodeToString(b)
|
||||||
|
return cookie
|
||||||
|
}
|
||||||
|
|
||||||
|
func Md5Str(str string) string {
|
||||||
|
hashBytes2 := md5.Sum([]byte(str))
|
||||||
|
hashString2 := hex.EncodeToString(hashBytes2[:]) // 注意数组转切片的[:]
|
||||||
|
return hashString2
|
||||||
|
}
|
||||||
|
|
||||||
|
func HashUserPass(user *TabUser_) {
|
||||||
|
switch ConfigsUser.PassHashType {
|
||||||
|
case "text":
|
||||||
|
break
|
||||||
|
case "md5":
|
||||||
|
user.Pass = Md5Str(user.Pass)
|
||||||
|
|
||||||
|
case "md5salt":
|
||||||
|
if user.Salt == "" {
|
||||||
|
user.Salt = RandStr32()
|
||||||
|
}
|
||||||
|
user.Pass = Md5Str(Md5Str(user.Pass) + user.Salt)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func IsExpired(expireTime time.Time) bool {
|
||||||
|
return expireTime.Before(time.Now())
|
||||||
|
}
|
||||||
|
|
||||||
|
func CheckCookiesAndUpdate(cookie *TabCookie_) bool {
|
||||||
|
if !IsExpired(cookie.ExpiresAt) {
|
||||||
|
if cookie.Remember {
|
||||||
|
cookiewhere := TabCookie_{
|
||||||
|
ID: cookie.ID,
|
||||||
|
}
|
||||||
|
cookieupdata := TabCookie_{
|
||||||
|
UpdatedAt: time.Now(),
|
||||||
|
ExpiresAt: time.Now().Add(time.Duration(ConfigsUser.CookieTimeout) * time.Second),
|
||||||
|
}
|
||||||
|
DB.Where(&cookiewhere).Updates(&cookieupdata)
|
||||||
|
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
} else {
|
||||||
|
//以过期
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
//return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// 判断邮箱是否合法
|
||||||
|
func IsEmailValid(email string) bool {
|
||||||
|
// 正则表达式(覆盖 99% 常见邮箱格式)
|
||||||
|
pattern := `^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$`
|
||||||
|
regex := regexp.MustCompile(pattern)
|
||||||
|
return regex.MatchString(email)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 判断字符串是否包含标点符号
|
||||||
|
func IsContainsSpecialChar(str string) bool {
|
||||||
|
specialChars := "!@#$%^&*()-+={}[]|\\:;\"'<>,.?/"
|
||||||
|
return strings.ContainsAny(str, specialChars)
|
||||||
|
}
|
||||||
@@ -0,0 +1,58 @@
|
|||||||
|
package models
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto"
|
||||||
|
"encoding/hex"
|
||||||
|
"io"
|
||||||
|
"mime/multipart"
|
||||||
|
"net/http"
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
// 判断文件是否存在
|
||||||
|
func FileExists(path string) bool {
|
||||||
|
_, err := os.Stat(path)
|
||||||
|
if err != nil {
|
||||||
|
return !os.IsNotExist(err)
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// 计算文件的哈希
|
||||||
|
func SHA256HashFile(file_head *multipart.FileHeader) (string, error) {
|
||||||
|
// 打开文件
|
||||||
|
file, err := file_head.Open()
|
||||||
|
if err != nil {
|
||||||
|
return "foen error", err
|
||||||
|
}
|
||||||
|
defer file.Close()
|
||||||
|
|
||||||
|
hasher := crypto.SHA256.New()
|
||||||
|
|
||||||
|
// 从文件流中读取并计算哈希
|
||||||
|
_, err = io.Copy(hasher, file)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
hashBytes := hasher.Sum(nil)
|
||||||
|
return hex.EncodeToString(hashBytes), nil
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取文件mime
|
||||||
|
func GetFileMime(file_head *multipart.FileHeader) (string, error) {
|
||||||
|
file, err := file_head.Open()
|
||||||
|
if err != nil {
|
||||||
|
return "foen error", err
|
||||||
|
}
|
||||||
|
defer file.Close()
|
||||||
|
|
||||||
|
// 读取前512字节用于MIME检测
|
||||||
|
buffer := make([]byte, 512)
|
||||||
|
io.ReadFull(file, buffer)
|
||||||
|
mimeType := http.DetectContentType(buffer)
|
||||||
|
|
||||||
|
return mimeType, nil
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,50 @@
|
|||||||
|
package models
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
|
// GetRealIP 获取真实IP(处理代理)
|
||||||
|
func GetRealIP(c *gin.Context) string {
|
||||||
|
// 优先级顺序
|
||||||
|
headers := []string{
|
||||||
|
"CF-Connecting-IP", // Cloudflare
|
||||||
|
"True-Client-IP",
|
||||||
|
"X-Forwarded-For",
|
||||||
|
"X-Real-IP",
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, header := range headers {
|
||||||
|
if ip := c.GetHeader(header); ip != "" {
|
||||||
|
// 处理多个IP的情况(如 X-Forwarded-For: client, proxy1, proxy2)
|
||||||
|
if strings.Contains(ip, ",") {
|
||||||
|
ips := strings.Split(ip, ",")
|
||||||
|
ip = strings.TrimSpace(ips[0])
|
||||||
|
}
|
||||||
|
|
||||||
|
if net.ParseIP(ip) != nil {
|
||||||
|
return ip
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 最后使用Gin的ClientIP方法
|
||||||
|
return c.ClientIP()
|
||||||
|
}
|
||||||
|
|
||||||
|
func LogAdd(c *gin.Context, msg string) {
|
||||||
|
|
||||||
|
var logtemp APIRequestLog_
|
||||||
|
|
||||||
|
logtemp.IPAddress = GetRealIP(c)
|
||||||
|
logtemp.Path = c.Request.URL.Path
|
||||||
|
logtemp.Method = c.Request.Method
|
||||||
|
logtemp.Message = msg
|
||||||
|
|
||||||
|
//fmt.Println(logtemp)
|
||||||
|
DB.Create(&logtemp)
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,167 @@
|
|||||||
|
package models
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/glebarez/sqlite"
|
||||||
|
"gorm.io/datatypes"
|
||||||
|
"gorm.io/driver/mysql"
|
||||||
|
"gorm.io/driver/postgres"
|
||||||
|
"gorm.io/gorm"
|
||||||
|
)
|
||||||
|
|
||||||
|
var DB *gorm.DB
|
||||||
|
|
||||||
|
type TabFileInfo_ struct {
|
||||||
|
ID uint `gorm:"primaryKey;autoIncrement"`
|
||||||
|
Name string `gorm:"not null;size:256;index"` // 前端报告的文件名
|
||||||
|
Path string `gorm:"not null;size:300"` //
|
||||||
|
Sha256 string `gorm:"not null;size:64;index"` //
|
||||||
|
Mime string `gorm:"size:64;index"`
|
||||||
|
Type string `gorm:"size:64;index"`
|
||||||
|
Const uint `gorm:"default:1;index"`
|
||||||
|
Per uint `gorm:"default:1"`
|
||||||
|
UserID uint `gorm:"not null;index"`
|
||||||
|
Date time.Time `gorm:"type:datetime;default:CURRENT_TIMESTAMP"` // 默认当前时间
|
||||||
|
}
|
||||||
|
|
||||||
|
type TabUser_ struct {
|
||||||
|
ID uint `gorm:"primaryKey;autoIncrement"` // 自增主键
|
||||||
|
Name string `gorm:"size:100;uniqueIndex"` // 唯一约束索引
|
||||||
|
Email string `gorm:"size:255;index"` // 字符串长度限制100 索引
|
||||||
|
Pass string `gorm:"size:128"` // 建议存储哈希后的密码
|
||||||
|
Type string `gorm:"size:64;default:user"` //
|
||||||
|
Salt string `gorm:"size:64;"`
|
||||||
|
Date time.Time `gorm:"type:datetime;default:CURRENT_TIMESTAMP"` // 默认当前时间
|
||||||
|
}
|
||||||
|
|
||||||
|
type TabUserGroups_ struct {
|
||||||
|
ID uint `gorm:"primaryKey;autoIncrement"` // 自增主键
|
||||||
|
Name string `gorm:"size:100;uniqueIndex"` // 唯一约束索引
|
||||||
|
Email string `gorm:"size:255;index"` // 字符串长度限制100 索引
|
||||||
|
Type string `gorm:"size:64;default:usergroup"` //
|
||||||
|
Date time.Time `gorm:"type:datetime;default:CURRENT_TIMESTAMP"` // 默认当前时间
|
||||||
|
}
|
||||||
|
|
||||||
|
type TabUserGroupBinds_ struct {
|
||||||
|
ID uint `gorm:"primaryKey;autoIncrement"` // 自增主键
|
||||||
|
UserID uint `gorm:"index"`
|
||||||
|
GroupID uint `gorm:"index"`
|
||||||
|
Date time.Time `gorm:"type:datetime;default:CURRENT_TIMESTAMP"` // 默认当前时间
|
||||||
|
}
|
||||||
|
|
||||||
|
type TabUserInfo_ struct {
|
||||||
|
ID uint `gorm:"primaryKey;autoIncrement"`
|
||||||
|
UserID uint `gorm:"not null;uniqueIndex"`
|
||||||
|
FirstName string `gorm:"size:50;null"`
|
||||||
|
Username string `gorm:"size:30;null"`
|
||||||
|
Birthdate time.Time `gorm:"type:datetime;null"`
|
||||||
|
Gender string `gorm:"type:char(1);check:gender IN ('M', 'F', 'U');default:'U'"`
|
||||||
|
AvatarPath string `gorm:"size:255"`
|
||||||
|
Region string `gorm:"size:50"`
|
||||||
|
Language string `gorm:"size:10;default:'zh-CN'"`
|
||||||
|
CreatedAt time.Time `gorm:"type:datetime;default:CURRENT_TIMESTAMP;column:created_at"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// var def_user_info = User_info{
|
||||||
|
// ID:0,
|
||||||
|
// UserID:0,
|
||||||
|
// }
|
||||||
|
|
||||||
|
type TabCookie_ struct {
|
||||||
|
ID uint `gorm:"primaryKey;autoIncrement"`
|
||||||
|
UserID uint `gorm:"not null"`
|
||||||
|
Name string `gorm:"size:255;not null;index"`
|
||||||
|
Value string `gorm:"size:255;not null;index"`
|
||||||
|
ExpiresAt time.Time `gorm:"type:datetime;index"`
|
||||||
|
CreatedAt time.Time `gorm:"type:datetime;not null;default:CURRENT_TIMESTAMP"`
|
||||||
|
UpdatedAt time.Time `gorm:"type:datetime;index;not null;default:CURRENT_TIMESTAMP"`
|
||||||
|
Remember bool `gorm:"default:false"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type APIRequestLog_ struct {
|
||||||
|
ID int64 `gorm:"primaryKey;column:id" json:"id"`
|
||||||
|
IPAddress string `gorm:"column:ip_address;size:45;not null" json:"ip_address"`
|
||||||
|
Path string `gorm:"column:path;size:500;not null" json:"path"`
|
||||||
|
Method string `gorm:"column:method;size:10;not null" json:"method"`
|
||||||
|
StatusCode int `gorm:"column:status_code;index" json:"status_code"`
|
||||||
|
Message string `gorm:"column:error_message;type:text" json:"error_message"`
|
||||||
|
CreatedAt time.Time `gorm:"column:created_at;type:datetime;default:CURRENT_TIMESTAMP" json:"created_at"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type TabPurchaseOrder struct {
|
||||||
|
ID uint `gorm:"primarykey"`
|
||||||
|
UserID uint `gorm:"not null"`
|
||||||
|
Title string `gorm:"size:200;comment:标题"`
|
||||||
|
Remark string `gorm:"type:text;comment:备注"`
|
||||||
|
Photos datatypes.JSON `gorm:"type:json;comment:照片哈希数组"`
|
||||||
|
Link string `gorm:"size:1000;comment:链接"`
|
||||||
|
PartName string `gorm:"size:200;not null;comment:物品名称"`
|
||||||
|
Styles string `gorm:"type:text;comment:样式数组"`
|
||||||
|
//Costs datatypes.JSON `gorm:"type:json;comment:费用明细数组"`
|
||||||
|
UpdateTime *time.Time `gorm:"type:datetime;autoUpdateTime;comment:更新时间"`
|
||||||
|
TrackingNumber string `gorm:"size:100;Index;comment:快递单号"`
|
||||||
|
OrderStatus string `gorm:"default:1;comment:订单状态"`
|
||||||
|
|
||||||
|
CreatedAt *time.Time `gorm:"type:datetime;autoCreateTime"`
|
||||||
|
UpdatedAt *time.Time `gorm:"type:datetime;autoUpdateTime"`
|
||||||
|
DeletedAt gorm.DeletedAt `gorm:"index"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type TabPurchaseCosts struct {
|
||||||
|
ID uint `gorm:"primarykey"`
|
||||||
|
OrderID uint `gorm:"not null"`
|
||||||
|
UserID uint `gorm:"not null"`
|
||||||
|
Price int `gorm:"not null"`
|
||||||
|
Quantity int `gorm:"not null"`
|
||||||
|
CreatedAt *time.Time `gorm:"type:datetime;autoCreateTime"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func DatabaseInit() error {
|
||||||
|
var err error
|
||||||
|
fmt.Println("database_init")
|
||||||
|
DatabaseConfigs := Configs["database"].(map[string]interface{})
|
||||||
|
|
||||||
|
if DatabaseConfigs["type"].(string) == "sqlite" {
|
||||||
|
//sqlite init
|
||||||
|
fmt.Println("sqlite")
|
||||||
|
DB, err = gorm.Open(sqlite.Open(DatabaseConfigs["path"].(string)), &gorm.Config{})
|
||||||
|
} else if DatabaseConfigs["type"].(string) == "mysql" {
|
||||||
|
//mysql init
|
||||||
|
fmt.Println("mysql")
|
||||||
|
dsn := fmt.Sprintf("%s:%s@tcp(%s:%s)/%s?charset=utf8mb4&parseTime=True&loc=Local", DatabaseConfigs["user"].(string), DatabaseConfigs["pass"].(string), DatabaseConfigs["host"].(string), DatabaseConfigs["port"].(string), DatabaseConfigs["name"].(string))
|
||||||
|
DB, err = gorm.Open(mysql.Open(dsn), &gorm.Config{})
|
||||||
|
} else if DatabaseConfigs["type"].(string) == "pg" {
|
||||||
|
//postgresql init
|
||||||
|
fmt.Println("postgresql")
|
||||||
|
dsn := fmt.Sprintf("host=%s user=%s password=%s dbname=%s port=%s sslmode=disable TimeZone=Asia/Shanghai", DatabaseConfigs["host"].(string), DatabaseConfigs["user"].(string), DatabaseConfigs["pass"].(string), DatabaseConfigs["name"].(string), DatabaseConfigs["port"].(string))
|
||||||
|
DB, err = gorm.Open(postgres.Open(dsn), &gorm.Config{})
|
||||||
|
}
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
panic("数据库连接失败")
|
||||||
|
}
|
||||||
|
|
||||||
|
// 自动创建表结构
|
||||||
|
DB.AutoMigrate(&TabUser_{})
|
||||||
|
|
||||||
|
DB.AutoMigrate(&TabUserGroups_{})
|
||||||
|
|
||||||
|
DB.AutoMigrate(&TabUserGroupBinds_{})
|
||||||
|
|
||||||
|
DB.AutoMigrate(&TabUserInfo_{})
|
||||||
|
|
||||||
|
DB.AutoMigrate(&TabCookie_{})
|
||||||
|
|
||||||
|
DB.AutoMigrate(&TabFileInfo_{})
|
||||||
|
|
||||||
|
DB.AutoMigrate(&APIRequestLog_{})
|
||||||
|
|
||||||
|
DB.AutoMigrate(&TabPurchaseOrder{})
|
||||||
|
|
||||||
|
DB.AutoMigrate(&TabPurchaseCosts{})
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
@@ -0,0 +1,62 @@
|
|||||||
|
package routers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
|
var ErrorCode map[string]interface{}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
//读取默认配置
|
||||||
|
fmt.Println("尝试读取错误码文件")
|
||||||
|
data, err := os.ReadFile("./defConfig/errorCodes.json")
|
||||||
|
if err != nil {
|
||||||
|
|
||||||
|
fmt.Println("读取错误码文件失败", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := json.Unmarshal(data, &ErrorCode); err != nil {
|
||||||
|
fmt.Println("解析错误码文件失败", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// 把数据分离成cookie和json
|
||||||
|
func SeparateData(ctx *gin.Context) (map[string]interface{}, string) {
|
||||||
|
var jsonData map[string]interface{}
|
||||||
|
|
||||||
|
if err := ctx.ShouldBindJSON(&jsonData); err == nil {
|
||||||
|
//分离数据
|
||||||
|
cookie, ok := jsonData["userCookieValue"].(string)
|
||||||
|
if !ok {
|
||||||
|
cookie = ""
|
||||||
|
}
|
||||||
|
|
||||||
|
data, ok := jsonData["data"].(map[string]interface{})
|
||||||
|
if !ok {
|
||||||
|
data = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return data, cookie
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, ""
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func ApiRoot(r *gin.RouterGroup) {
|
||||||
|
|
||||||
|
ApiStatic(r.Group("/static"))
|
||||||
|
ApiUser(r.Group("/users"))
|
||||||
|
ApiFiles(r.Group("/files"))
|
||||||
|
ApiPurchase(r.Group("/purchase"))
|
||||||
|
|
||||||
|
r.GET("/", func(ctx *gin.Context) {
|
||||||
|
ReturnJson(ctx, "apiOK", nil)
|
||||||
|
})
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,179 @@
|
|||||||
|
package routers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"ops/models"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
"github.com/mitchellh/mapstructure"
|
||||||
|
"gorm.io/datatypes"
|
||||||
|
)
|
||||||
|
|
||||||
|
type CostItem struct {
|
||||||
|
Cost int `json:"cost"` // 必须,非负
|
||||||
|
CostT int `json:"costt"` // 必须,非负
|
||||||
|
CurrencyType string `json:"currencytype"` // 必须
|
||||||
|
Int int `json:"int"` // 必须
|
||||||
|
Type string `json:"type"` // 必须
|
||||||
|
}
|
||||||
|
type From_purchase_addorder struct {
|
||||||
|
Costs []CostItem `json:"costs"` //
|
||||||
|
Link string `json:"link"` // 可选
|
||||||
|
OrderStatus string `json:"order_status"` //
|
||||||
|
PartName string `json:"partname"` // 可选
|
||||||
|
Photos []string `json:"photos"` // 可选
|
||||||
|
Remark string `json:"remark"` // 可选
|
||||||
|
Styles string `json:"styles"` // 可选
|
||||||
|
Title string `json:"title"` // 必须
|
||||||
|
TrackingNumber string `json:"tracking_number"` // 可选
|
||||||
|
UpdateTime string `json:"update_time"` // 可选
|
||||||
|
}
|
||||||
|
|
||||||
|
func ApiPurchase(r *gin.RouterGroup) {
|
||||||
|
|
||||||
|
r.POST("/getorders", func(ctx *gin.Context) {
|
||||||
|
isAuth, user, data := AuthenticationAuthority(ctx)
|
||||||
|
if isAuth {
|
||||||
|
fmt.Println(user)
|
||||||
|
// DebugPrintJson(data)
|
||||||
|
|
||||||
|
type From_purchase_getorders struct {
|
||||||
|
Search string
|
||||||
|
Entries int
|
||||||
|
Page int
|
||||||
|
}
|
||||||
|
|
||||||
|
var jsondata From_purchase_getorders
|
||||||
|
if err := mapstructure.Decode(data, &jsondata); err == nil {
|
||||||
|
//fmt.Println(jsondata)
|
||||||
|
|
||||||
|
is_data_ok := true
|
||||||
|
|
||||||
|
if jsondata.Entries <= 0 || jsondata.Entries > 300 {
|
||||||
|
is_data_ok = false
|
||||||
|
}
|
||||||
|
if jsondata.Page <= 0 {
|
||||||
|
is_data_ok = false
|
||||||
|
}
|
||||||
|
|
||||||
|
if is_data_ok {
|
||||||
|
|
||||||
|
//读取有多少条目
|
||||||
|
var count int64
|
||||||
|
models.DB.Model(&models.TabPurchaseOrder{}).Count(&count)
|
||||||
|
//fmt.Println(count)
|
||||||
|
|
||||||
|
//读取条目
|
||||||
|
var getorders []models.TabPurchaseOrder
|
||||||
|
models.DB.Order("created_at DESC").Offset(jsondata.Entries * (jsondata.Page - 1)).Limit(jsondata.Entries).Find(&getorders)
|
||||||
|
|
||||||
|
ReturnJson(ctx, "apiOK", map[string]interface{}{
|
||||||
|
"all_count": count,
|
||||||
|
"all_orders": getorders,
|
||||||
|
})
|
||||||
|
|
||||||
|
} else {
|
||||||
|
ReturnJson(ctx, "jsonErr", nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
ReturnJson(ctx, "jsonErr", nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
ReturnJson(ctx, "userCookieError", nil)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
r.POST("/addorder", func(ctx *gin.Context) {
|
||||||
|
isAuth, user, data := AuthenticationAuthority(ctx)
|
||||||
|
if isAuth {
|
||||||
|
|
||||||
|
//需要处理提交的数据,接口有固定的数据格式,不允许乱搞
|
||||||
|
//fmt.Println(isAuth)
|
||||||
|
//fmt.Println(user)
|
||||||
|
//DebugPrintJson(data)
|
||||||
|
var jsondata From_purchase_addorder
|
||||||
|
if err := mapstructure.Decode(data, &jsondata); err == nil {
|
||||||
|
|
||||||
|
//fmt.Println("转换后数据:\n", jsondata)
|
||||||
|
|
||||||
|
//数据比较混乱 在这里校验
|
||||||
|
|
||||||
|
//判断标题不为空
|
||||||
|
is_data_ok := true
|
||||||
|
if jsondata.Title == "" {
|
||||||
|
is_data_ok = false
|
||||||
|
}
|
||||||
|
|
||||||
|
//判断数量与价格是否为负数
|
||||||
|
for i := 0; i < len(jsondata.Costs); i++ {
|
||||||
|
if jsondata.Costs[i].Cost <= 0 {
|
||||||
|
is_data_ok = false
|
||||||
|
}
|
||||||
|
if jsondata.Costs[i].Int <= 0 {
|
||||||
|
is_data_ok = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//判断图片是否为哈希值
|
||||||
|
for i := 0; i < len(jsondata.Photos); i++ {
|
||||||
|
//判断字符串是否包含标点符号
|
||||||
|
if models.IsContainsSpecialChar(jsondata.Photos[i]) {
|
||||||
|
is_data_ok = false
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
//判断时间字符串是否合法
|
||||||
|
uptime, e := models.StringToTimePtr(jsondata.UpdateTime)
|
||||||
|
if e != nil {
|
||||||
|
is_data_ok = false
|
||||||
|
}
|
||||||
|
|
||||||
|
if is_data_ok {
|
||||||
|
//校验通过
|
||||||
|
//载入数据库
|
||||||
|
|
||||||
|
photos, _ := json.Marshal(jsondata.Photos) //把图片数组转换成字符串
|
||||||
|
new_data := models.TabPurchaseOrder{
|
||||||
|
UserID: user.ID,
|
||||||
|
Title: jsondata.Title,
|
||||||
|
Remark: jsondata.Remark,
|
||||||
|
Photos: datatypes.JSON(photos),
|
||||||
|
Link: jsondata.Link,
|
||||||
|
PartName: jsondata.PartName,
|
||||||
|
Styles: jsondata.Styles,
|
||||||
|
UpdateTime: uptime,
|
||||||
|
TrackingNumber: jsondata.TrackingNumber,
|
||||||
|
OrderStatus: jsondata.OrderStatus,
|
||||||
|
}
|
||||||
|
models.DB.Create(&new_data)
|
||||||
|
|
||||||
|
for i := 0; i < len(jsondata.Costs); i++ {
|
||||||
|
new_cost_data := models.TabPurchaseCosts{
|
||||||
|
Price: jsondata.Costs[i].Cost,
|
||||||
|
Quantity: jsondata.Costs[i].Int,
|
||||||
|
UserID: user.ID,
|
||||||
|
OrderID: new_data.ID,
|
||||||
|
}
|
||||||
|
models.DB.Create(&new_cost_data)
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
ReturnJson(ctx, "jsonErr", nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
ReturnJson(ctx, "jsonErr", nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
ReturnJson(ctx, "userCookieError", nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
ReturnJson(ctx, "apiErr", nil)
|
||||||
|
})
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
package routers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"ops/models"
|
||||||
|
"path"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
|
//处理api的静态内容
|
||||||
|
|
||||||
|
func ApiStatic(r *gin.RouterGroup) {
|
||||||
|
r.GET("/avatar/:filename", func(ctx *gin.Context) {
|
||||||
|
filename := ctx.Param("filename")
|
||||||
|
dst := path.Join(models.ConfigsFile.Pahts["avatar"], filename)
|
||||||
|
if models.FileExists(dst) {
|
||||||
|
ctx.File(dst)
|
||||||
|
} else {
|
||||||
|
//找不到文件
|
||||||
|
ctx.String(404, "file not found")
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
})
|
||||||
|
}
|
||||||
@@ -0,0 +1,541 @@
|
|||||||
|
package routers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"ops/models"
|
||||||
|
"path"
|
||||||
|
"strconv"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
"github.com/mitchellh/mapstructure"
|
||||||
|
)
|
||||||
|
|
||||||
|
func ApiInit() {
|
||||||
|
//用户模块初始化init
|
||||||
|
fmt.Println("users init")
|
||||||
|
|
||||||
|
//创建admin用户
|
||||||
|
var user models.TabUser_
|
||||||
|
user.Name = "admin"
|
||||||
|
|
||||||
|
if models.DB.Where(&user).First(&user).Error == nil {
|
||||||
|
|
||||||
|
} else {
|
||||||
|
//fmt.Println("用户不存在")
|
||||||
|
|
||||||
|
//对密码加盐
|
||||||
|
user.Salt = models.RandStr32()
|
||||||
|
user.Pass = "adminpassword"
|
||||||
|
models.HashUserPass(&user)
|
||||||
|
models.DB.Create(&user) // 传入指针
|
||||||
|
}
|
||||||
|
|
||||||
|
//创建admin group
|
||||||
|
var usergroup models.TabUserGroups_
|
||||||
|
usergroup.Name = "admins"
|
||||||
|
if models.DB.Where(&usergroup).First(&usergroup).Error == nil {
|
||||||
|
|
||||||
|
} else {
|
||||||
|
fmt.Println("用户组不存在")
|
||||||
|
models.DB.Create(&usergroup) // 传入指针
|
||||||
|
}
|
||||||
|
|
||||||
|
//创建用户与用户组绑定
|
||||||
|
var usergroupbind models.TabUserGroupBinds_
|
||||||
|
usergroupbind.UserID = user.ID
|
||||||
|
usergroupbind.GroupID = usergroup.ID
|
||||||
|
|
||||||
|
if models.DB.Where(&usergroupbind).First(&usergroupbind).Error == nil {
|
||||||
|
|
||||||
|
} else {
|
||||||
|
models.DB.Create(&usergroupbind) // 传入指针
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
type From_user_add struct {
|
||||||
|
Useremail string `json:"useremail"`
|
||||||
|
Username string `json:"username"`
|
||||||
|
Userpass string `json:"userpass"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type From_user_login struct {
|
||||||
|
Username string `json:"username"`
|
||||||
|
Userpass string `json:"userpass"`
|
||||||
|
Remember bool `json:"remember"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type From_user_updateinfo struct {
|
||||||
|
Username string `json:"username"`
|
||||||
|
Remark string `json:"remark"`
|
||||||
|
Birthday string `json:"birthday"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type From_user_changeemail struct {
|
||||||
|
Newemail string `json:"newemail"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type From_user_changepass struct {
|
||||||
|
Oldpass string `json:"oldpass"`
|
||||||
|
Newpass string `json:"newpass"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func AuthenticationAuthorityFromCookie(c string) (*models.TabUser_, error) {
|
||||||
|
|
||||||
|
if c != "" {
|
||||||
|
cookie := models.TabCookie_{
|
||||||
|
Value: c,
|
||||||
|
}
|
||||||
|
if models.DB.Where(&cookie).First(&cookie).Error == nil {
|
||||||
|
//找到cookie,验证cookie有效性,以及更新cookie
|
||||||
|
if models.CheckCookiesAndUpdate(&cookie) {
|
||||||
|
//cookie有效
|
||||||
|
//载入user
|
||||||
|
user := models.TabUser_{
|
||||||
|
ID: cookie.UserID,
|
||||||
|
}
|
||||||
|
models.DB.Where(&user).First(&user)
|
||||||
|
return &user, nil
|
||||||
|
} else {
|
||||||
|
return nil, errors.New("cookie 过期")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return nil, errors.New("cookie Not Fund")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return nil, errors.New("cookie 参数错误")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func AuthenticationAuthority(ctx *gin.Context) (bool, models.TabUser_, map[string]interface{}) {
|
||||||
|
var user models.TabUser_
|
||||||
|
|
||||||
|
data, cookieval := SeparateData(ctx)
|
||||||
|
//fmt.Println("cookieis" + cookieval)
|
||||||
|
if cookieval != "" {
|
||||||
|
cookie := models.TabCookie_{
|
||||||
|
Value: cookieval,
|
||||||
|
}
|
||||||
|
if models.DB.Where(&cookie).First(&cookie).Error == nil {
|
||||||
|
//找到cookie,验证cookie有效性,以及更新cookie
|
||||||
|
if models.CheckCookiesAndUpdate(&cookie) {
|
||||||
|
//cookie有效
|
||||||
|
//载入user
|
||||||
|
user := models.TabUser_{
|
||||||
|
ID: cookie.UserID,
|
||||||
|
}
|
||||||
|
models.DB.Where(&user).First(&user)
|
||||||
|
|
||||||
|
return true, user, data
|
||||||
|
|
||||||
|
} else {
|
||||||
|
ReturnJson(ctx, "userCookieExpired", nil)
|
||||||
|
return false, user, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
ReturnJson(ctx, "userCookieNotFund", nil)
|
||||||
|
return false, user, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
ReturnJson(ctx, "userCookieError", nil)
|
||||||
|
return false, user, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
//return false, user
|
||||||
|
}
|
||||||
|
|
||||||
|
func ApiUser(r *gin.RouterGroup) {
|
||||||
|
|
||||||
|
r.GET("/test", func(ctx *gin.Context) {
|
||||||
|
ReturnJson(ctx, "apiOK", nil)
|
||||||
|
})
|
||||||
|
r.POST("/test", func(ctx *gin.Context) {
|
||||||
|
ReturnJson(ctx, "apiOK", nil)
|
||||||
|
})
|
||||||
|
|
||||||
|
//修改用户密码
|
||||||
|
r.POST("/changePassword", func(ctx *gin.Context) {
|
||||||
|
isAuth, user, data := AuthenticationAuthority(ctx)
|
||||||
|
if isAuth {
|
||||||
|
var jsonData From_user_changepass
|
||||||
|
if err := mapstructure.Decode(data, &jsonData); err == nil {
|
||||||
|
//验证旧密码
|
||||||
|
fmt.Println(user)
|
||||||
|
//转换旧密码
|
||||||
|
olduser := models.TabUser_{
|
||||||
|
Pass: jsonData.Oldpass,
|
||||||
|
Salt: user.Salt,
|
||||||
|
}
|
||||||
|
models.HashUserPass(&olduser)
|
||||||
|
if olduser.Pass == user.Pass {
|
||||||
|
//旧密码正确,更新新密码
|
||||||
|
var userupdate models.TabUser_
|
||||||
|
userupdate.Pass = jsonData.Newpass
|
||||||
|
userupdate.Salt = models.RandStr32()
|
||||||
|
models.HashUserPass(&userupdate)
|
||||||
|
models.DB.Model(&user).Updates(&userupdate)
|
||||||
|
ReturnJson(ctx, "apiOK", nil)
|
||||||
|
} else {
|
||||||
|
//旧密码错误
|
||||||
|
ReturnJson(ctx, "userPassIncorrect", nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
ReturnJson(ctx, "jsonErr", nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
//更新用户邮箱
|
||||||
|
r.POST("/changeEmail", func(ctx *gin.Context) {
|
||||||
|
isAuth, user, data := AuthenticationAuthority(ctx)
|
||||||
|
if isAuth {
|
||||||
|
var jsonData From_user_changeemail
|
||||||
|
if err := mapstructure.Decode(data, &jsonData); err == nil {
|
||||||
|
//判断新邮箱格式
|
||||||
|
if models.IsEmailValid(jsonData.Newemail) {
|
||||||
|
var userupdate models.TabUser_
|
||||||
|
userupdate.Email = jsonData.Newemail
|
||||||
|
models.DB.Model(&user).Updates(&userupdate)
|
||||||
|
ReturnJson(ctx, "apiOK", nil)
|
||||||
|
} else {
|
||||||
|
ReturnJson(ctx, "userEmailFormatError", nil)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
ReturnJson(ctx, "jsonErr", nil)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
//修改用户头像
|
||||||
|
r.POST("/updateAvatar", func(ctx *gin.Context) {
|
||||||
|
cookie := ctx.PostForm("cookie")
|
||||||
|
user, err := AuthenticationAuthorityFromCookie(cookie)
|
||||||
|
if err == nil {
|
||||||
|
file, err := ctx.FormFile("file")
|
||||||
|
if err == nil {
|
||||||
|
if file.Filename != "" {
|
||||||
|
//限制文件大小
|
||||||
|
if file.Size > 512 {
|
||||||
|
//头像裁剪过限制1M应该差不多
|
||||||
|
if file.Size < 1048576 {
|
||||||
|
|
||||||
|
//判断mime
|
||||||
|
mimeType, err := models.GetFileMime(file)
|
||||||
|
if err == nil {
|
||||||
|
|
||||||
|
file_extname := models.ConfigsFile.AllowImageMime[mimeType]
|
||||||
|
if file_extname != "" {
|
||||||
|
|
||||||
|
//haxi文件
|
||||||
|
|
||||||
|
file_hashi_name, err := models.SHA256HashFile(file)
|
||||||
|
if err == nil {
|
||||||
|
|
||||||
|
dst := path.Join(models.ConfigsFile.Pahts["avatar"], file_hashi_name+file_extname)
|
||||||
|
|
||||||
|
var is_save_ok = false
|
||||||
|
//判断文件是否存在避免重复保存
|
||||||
|
if models.FileExists(dst) {
|
||||||
|
//fmt.Println("文件存在")
|
||||||
|
is_save_ok = true
|
||||||
|
ReturnJson(ctx, "apiOK", nil)
|
||||||
|
} else {
|
||||||
|
//fmt.Println("文件no存在")
|
||||||
|
ferr := ctx.SaveUploadedFile(file, dst)
|
||||||
|
if ferr == nil {
|
||||||
|
//文件保存成功
|
||||||
|
//fmt.Print("save_ok")
|
||||||
|
is_save_ok = true
|
||||||
|
ReturnJson(ctx, "apiOK", nil)
|
||||||
|
} else {
|
||||||
|
fmt.Print(ferr)
|
||||||
|
ReturnJson(ctx, "postErr", nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
if is_save_ok {
|
||||||
|
//修改数据库内容
|
||||||
|
var user_info_fund models.TabUserInfo_
|
||||||
|
user_info_fund.UserID = user.ID
|
||||||
|
|
||||||
|
var user_update_avatar models.TabUserInfo_
|
||||||
|
user_update_avatar.AvatarPath = file_hashi_name + file_extname
|
||||||
|
|
||||||
|
models.DB.Where(&user_info_fund).Updates(&user_update_avatar)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
ReturnJson(ctx, "postErr", nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
ReturnJson(ctx, "file_mime_err", nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
ReturnJson(ctx, "postErr", nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
ReturnJson(ctx, "file_size_err", nil)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
ReturnJson(ctx, "file_size_err", nil)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
ReturnJson(ctx, "file_name_err", nil)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
ReturnJson(ctx, "file_get_err", nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
ReturnJson(ctx, "userCookieError", nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
})
|
||||||
|
|
||||||
|
//更新用户info
|
||||||
|
r.POST("/updateInfo", func(ctx *gin.Context) {
|
||||||
|
isAuth, user, data := AuthenticationAuthority(ctx)
|
||||||
|
if isAuth {
|
||||||
|
var jsonData From_user_updateinfo
|
||||||
|
|
||||||
|
if err := mapstructure.Decode(data, &jsonData); err == nil {
|
||||||
|
// fmt.Println("updateinfo data is", jsonData)
|
||||||
|
// fmt.Println(user)
|
||||||
|
t, err := time.Parse("2006-01-02", jsonData.Birthday)
|
||||||
|
if err == nil {
|
||||||
|
var userinfo models.TabUserInfo_
|
||||||
|
userinfo.UserID = user.ID
|
||||||
|
|
||||||
|
var userinfoupdate models.TabUserInfo_
|
||||||
|
userinfoupdate.UserID = user.ID
|
||||||
|
userinfoupdate.CreatedAt = time.Now()
|
||||||
|
userinfoupdate.Username = jsonData.Username
|
||||||
|
userinfoupdate.Birthdate = t
|
||||||
|
userinfoupdate.FirstName = jsonData.Remark
|
||||||
|
|
||||||
|
//先查找是否有记录
|
||||||
|
if models.DB.Where(&userinfo).First(&userinfo).Error == nil {
|
||||||
|
//有记录,更新
|
||||||
|
models.DB.Model(&userinfo).Updates(&userinfoupdate)
|
||||||
|
} else {
|
||||||
|
//无记录,创建
|
||||||
|
models.DB.Create(&userinfoupdate) // 传入指针
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
ReturnJson(ctx, "apiOK", nil)
|
||||||
|
|
||||||
|
} else {
|
||||||
|
ReturnJson(ctx, "jsonErr", nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
ReturnJson(ctx, "jsonErr", nil)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
})
|
||||||
|
|
||||||
|
//通过cookie获取用户info
|
||||||
|
r.POST("/getinfo", func(ctx *gin.Context) {
|
||||||
|
isAuth, user, _ := AuthenticationAuthority(ctx)
|
||||||
|
if isAuth {
|
||||||
|
//载入用户info
|
||||||
|
var userinfo models.TabUserInfo_
|
||||||
|
userinfo.UserID = user.ID
|
||||||
|
//fmt.Println(userInfo)
|
||||||
|
var redata map[string]interface{} = make(map[string]interface{})
|
||||||
|
if models.DB.Where(&userinfo).First(&userinfo).Error == nil {
|
||||||
|
redata["userInfo"] = userinfo
|
||||||
|
} else {
|
||||||
|
redata["userInfo"] = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
user.Pass = ""
|
||||||
|
user.Salt = ""
|
||||||
|
|
||||||
|
redata["user"] = user
|
||||||
|
|
||||||
|
ReturnJson(ctx, "apiOK", redata)
|
||||||
|
|
||||||
|
}
|
||||||
|
// _, cookieval := SeparateData(ctx)
|
||||||
|
// //fmt.Println("cookieis" + cookieval)
|
||||||
|
// if cookieval != "" {
|
||||||
|
// cookie := models.TabCookie_{
|
||||||
|
// Value: cookieval,
|
||||||
|
// }
|
||||||
|
// if models.DB.Where(&cookie).First(&cookie).Error == nil {
|
||||||
|
// //找到cookie,验证cookie有效性,以及更新cookie
|
||||||
|
// if models.CheckCookiesAndUpdate(&cookie) {
|
||||||
|
// //cookie有效
|
||||||
|
// //返回最新cookie
|
||||||
|
// redata := map[string]interface{}{
|
||||||
|
// "cookie": cookie,
|
||||||
|
// }
|
||||||
|
// //载入用户info
|
||||||
|
// userInfo := models.TabFileInfo_{
|
||||||
|
// UserID: cookie.UserID,
|
||||||
|
// }
|
||||||
|
// if models.DB.Where(&userInfo).First(&userInfo).Error == nil {
|
||||||
|
// redata["userInfo"] = userInfo
|
||||||
|
// } else {
|
||||||
|
// redata["userInfo"] = nil
|
||||||
|
// }
|
||||||
|
|
||||||
|
// //载入user
|
||||||
|
// user := models.TabUser_{
|
||||||
|
// ID: cookie.UserID,
|
||||||
|
// }
|
||||||
|
// models.DB.Where(&user).First(&user)
|
||||||
|
// user.Pass = ""
|
||||||
|
// user.Salt = ""
|
||||||
|
|
||||||
|
// redata["user"] = user
|
||||||
|
|
||||||
|
// ReturnJson(ctx, "apiOK", redata)
|
||||||
|
|
||||||
|
// } else {
|
||||||
|
// ReturnJson(ctx, "userCookieExpired", nil)
|
||||||
|
// }
|
||||||
|
|
||||||
|
// } else {
|
||||||
|
// ReturnJson(ctx, "userCookieNotFund", nil)
|
||||||
|
// }
|
||||||
|
|
||||||
|
// } else {
|
||||||
|
// ReturnJson(ctx, "userCookieError", nil)
|
||||||
|
// }
|
||||||
|
|
||||||
|
})
|
||||||
|
//用户登陆
|
||||||
|
r.POST("/login", func(ctx *gin.Context) {
|
||||||
|
var loginuser From_user_login
|
||||||
|
data, _ := SeparateData(ctx)
|
||||||
|
if data != nil {
|
||||||
|
if err := mapstructure.Decode(data, &loginuser); err == nil {
|
||||||
|
if loginuser.Username != "" && loginuser.Userpass != "" {
|
||||||
|
//传入的数据都ok,获取用户信息
|
||||||
|
|
||||||
|
getuser := models.TabUser_{
|
||||||
|
Name: loginuser.Username,
|
||||||
|
}
|
||||||
|
|
||||||
|
if models.DB.Where(&getuser).First(&getuser).Error == nil {
|
||||||
|
//倒入数据
|
||||||
|
user := models.TabUser_{
|
||||||
|
Pass: loginuser.Userpass, //密码明文
|
||||||
|
Salt: getuser.Salt, //保存的盐制
|
||||||
|
}
|
||||||
|
//哈希密
|
||||||
|
models.HashUserPass(&user)
|
||||||
|
if user.Pass == getuser.Pass {
|
||||||
|
//用户密码正确,生成cookie
|
||||||
|
cookie := models.TabCookie_{
|
||||||
|
UserID: getuser.ID,
|
||||||
|
Name: "login",
|
||||||
|
Value: models.RandStr32(),
|
||||||
|
CreatedAt: time.Now(),
|
||||||
|
UpdatedAt: time.Now(),
|
||||||
|
ExpiresAt: time.Now().Add(time.Duration(models.ConfigsUser.CookieTimeout) * time.Second), //计算过期时间,
|
||||||
|
Remember: loginuser.Remember,
|
||||||
|
}
|
||||||
|
models.DB.Create(&cookie) // 传入指针
|
||||||
|
|
||||||
|
redata := map[string]interface{}{
|
||||||
|
"cookie": cookie,
|
||||||
|
}
|
||||||
|
|
||||||
|
ReturnJson(ctx, "apiOK", redata)
|
||||||
|
} else {
|
||||||
|
ReturnJson(ctx, "userPassIncorrect", nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
//用户不存在
|
||||||
|
ReturnJson(ctx, "userNameNoFund", nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
ReturnJson(ctx, "jsonErr", nil)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
ReturnJson(ctx, "jsonErr", nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
ReturnJson(ctx, "postErr", nil)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
//用户注册
|
||||||
|
r.POST("/register", func(ctx *gin.Context) {
|
||||||
|
//转换传进来的数据
|
||||||
|
var jsonData From_user_add
|
||||||
|
|
||||||
|
data, _ := SeparateData(ctx)
|
||||||
|
|
||||||
|
if data != nil {
|
||||||
|
if err := mapstructure.Decode(data, &jsonData); err == nil {
|
||||||
|
//转换字段
|
||||||
|
newUser := models.TabUser_{
|
||||||
|
Name: jsonData.Username,
|
||||||
|
Email: jsonData.Useremail,
|
||||||
|
Pass: jsonData.Userpass, // 实际应替换为哈希值
|
||||||
|
Date: time.Now(),
|
||||||
|
// Date 字段无需赋值,数据库会自动填充默认值
|
||||||
|
}
|
||||||
|
if newUser.Name != "" && newUser.Pass != "" && newUser.Email != "" {
|
||||||
|
|
||||||
|
//用户名是唯一的,先读取是否有这个用户名
|
||||||
|
var user models.TabUser_
|
||||||
|
user.Name = newUser.Name
|
||||||
|
|
||||||
|
if models.DB.Where(&user).First(&user).Error == nil {
|
||||||
|
//fmt.Println("找到用户:", user.ID)
|
||||||
|
ReturnJson(ctx, "userNameDup", nil)
|
||||||
|
} else {
|
||||||
|
//fmt.Println("用户不存在")
|
||||||
|
|
||||||
|
//对密码加盐
|
||||||
|
newUser.Salt = models.RandStr32()
|
||||||
|
|
||||||
|
//对用户的密码进行哈希替换
|
||||||
|
models.HashUserPass(&newUser)
|
||||||
|
|
||||||
|
models.DB.Create(&newUser) // 传入指针
|
||||||
|
|
||||||
|
//创建用户后写一个log
|
||||||
|
|
||||||
|
models.LogAdd(ctx, "New user id:"+strconv.Itoa(int(newUser.ID)))
|
||||||
|
|
||||||
|
ReturnJson(ctx, "apiOK", nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
ReturnJson(ctx, "jsonErr", nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
ReturnJson(ctx, "jsonErr", nil)
|
||||||
|
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
ReturnJson(ctx, "postErr", nil)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
})
|
||||||
|
}
|
||||||
@@ -0,0 +1,174 @@
|
|||||||
|
package routers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
|
"ops/models"
|
||||||
|
"path"
|
||||||
|
"path/filepath"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
|
func file_save() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func ApiFiles(r *gin.RouterGroup) {
|
||||||
|
|
||||||
|
//getfile := r.Group("/get") //定义上传组
|
||||||
|
r.GET("/:mode/:hash", func(ctx *gin.Context) {
|
||||||
|
hash := ctx.Param("hash")
|
||||||
|
mode := ctx.Param("mode")
|
||||||
|
// filename := ctx.Param("filename")
|
||||||
|
// fmt.Println(filename)
|
||||||
|
|
||||||
|
download := false
|
||||||
|
isPartOK := false
|
||||||
|
|
||||||
|
if mode == "get" {
|
||||||
|
isPartOK = true
|
||||||
|
download = true
|
||||||
|
}
|
||||||
|
if mode == "download" {
|
||||||
|
isPartOK = true
|
||||||
|
download = false
|
||||||
|
}
|
||||||
|
if isPartOK {
|
||||||
|
file_info := models.TabFileInfo_{
|
||||||
|
Sha256: hash,
|
||||||
|
}
|
||||||
|
if models.DB.Where(&file_info).First(&file_info).Error == nil {
|
||||||
|
ReturnFile(ctx, &file_info, download)
|
||||||
|
} else {
|
||||||
|
//fmt.Println("not fund")
|
||||||
|
ReturnJson(ctx, "file_not_found", nil)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
ReturnJson(ctx, "file_part_err", nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
})
|
||||||
|
|
||||||
|
upload := r.Group("/upload") //定义上传组
|
||||||
|
//上传文件的总接口,能上传什么文件应该由后端决定,前端仅做相应限制
|
||||||
|
|
||||||
|
upload.POST("/image", func(ctx *gin.Context) {
|
||||||
|
|
||||||
|
cookie := ctx.PostForm("cookie") //首先需要判断用户是否登录
|
||||||
|
|
||||||
|
//通过cookie获取用户信息
|
||||||
|
user, err := AuthenticationAuthorityFromCookie(cookie)
|
||||||
|
if err == nil {
|
||||||
|
file, err := ctx.FormFile("file")
|
||||||
|
|
||||||
|
if err == nil {
|
||||||
|
if file.Filename != "" {
|
||||||
|
//限制文件大小
|
||||||
|
if file.Size > 512 {
|
||||||
|
if file.Size < int64(models.ConfigsFile.MaxSize) {
|
||||||
|
|
||||||
|
//判断文件mime是否合法
|
||||||
|
// 打开文件流
|
||||||
|
src_mime, _ := file.Open()
|
||||||
|
defer src_mime.Close()
|
||||||
|
// 读取前512字节用于MIME检测
|
||||||
|
buffer := make([]byte, 512)
|
||||||
|
io.ReadFull(src_mime, buffer)
|
||||||
|
// 检测MIME类型
|
||||||
|
mimeType := http.DetectContentType(buffer)
|
||||||
|
file_extname := models.ConfigsFile.AllowImageMime[mimeType]
|
||||||
|
if file_extname != "" {
|
||||||
|
filename := filepath.Base(file.Filename) // 防御性处理路径分隔符
|
||||||
|
// 计算哈希值
|
||||||
|
hash_str, err := models.SHA256HashFile(file)
|
||||||
|
if err == nil {
|
||||||
|
//fmt.Println(hash_str)
|
||||||
|
//fmt.Println(filename)
|
||||||
|
//这是上传的真实路径
|
||||||
|
dst := path.Join(models.ConfigsFile.Pahts["image"], hash_str)
|
||||||
|
//fmt.Println(dst)
|
||||||
|
//判断文件是否存在避免重复保存
|
||||||
|
if models.FileExists(dst) {
|
||||||
|
//fmt.Println("文件存在")
|
||||||
|
|
||||||
|
} else {
|
||||||
|
//fmt.Println("文件no存在")
|
||||||
|
ferr := ctx.SaveUploadedFile(file, dst)
|
||||||
|
if ferr == nil {
|
||||||
|
//文件保存成功
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
ReturnJson(ctx, "file_save_err", nil)
|
||||||
|
ctx.Abort() //end
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//记录到数据库
|
||||||
|
//先检查数据库有没有数据
|
||||||
|
fund_file_info := models.TabFileInfo_{
|
||||||
|
Name: filename,
|
||||||
|
Sha256: hash_str,
|
||||||
|
Mime: mimeType,
|
||||||
|
Type: "image",
|
||||||
|
UserID: user.ID,
|
||||||
|
}
|
||||||
|
fund_file_info2 := models.TabFileInfo_{}
|
||||||
|
|
||||||
|
models.DB.Where(&fund_file_info).Find(&fund_file_info2)
|
||||||
|
|
||||||
|
if fund_file_info2.ID != 0 {
|
||||||
|
fund_file_info2.Const += 1
|
||||||
|
models.DB.Where(&fund_file_info).Updates(&fund_file_info2)
|
||||||
|
} else {
|
||||||
|
fund_file_info.Path = dst
|
||||||
|
models.DB.Create(&fund_file_info) // 传入指针
|
||||||
|
}
|
||||||
|
|
||||||
|
//返回后台存储的URL
|
||||||
|
download_URL := path.Join("/api/files/download/", hash_str)
|
||||||
|
get_URL := path.Join("/api/files/get/", hash_str)
|
||||||
|
re := map[string]interface{}{
|
||||||
|
"download": download_URL,
|
||||||
|
"get": get_URL,
|
||||||
|
"hash": hash_str,
|
||||||
|
}
|
||||||
|
|
||||||
|
ReturnJson(ctx, "apiOK", re)
|
||||||
|
|
||||||
|
} else {
|
||||||
|
ReturnJson(ctx, "file_hash_err", nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
ReturnJson(ctx, "file_mime_err", nil)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
ReturnJson(ctx, "file_size_err", nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
ReturnJson(ctx, "file_size_err", nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
ReturnJson(ctx, "file_name_err", nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
ReturnJson(ctx, "file_get_err", nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
ReturnJson(ctx, "userCookieError", nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
//ReturnJson(ctx, "apiErr", nil)
|
||||||
|
})
|
||||||
|
|
||||||
|
// r.GET("/upload", func(ctx *gin.Context) {
|
||||||
|
// ReturnJson(ctx, "apiOK", nil)
|
||||||
|
// })
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
package routers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"ops/models"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
|
func DebugPrintJson(data map[string]interface{}) {
|
||||||
|
p, _ := json.MarshalIndent(data, "", " ")
|
||||||
|
fmt.Println("\n", string(p))
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func ReturnJson(ctx *gin.Context, errMsg string, data map[string]interface{}) {
|
||||||
|
var errCode = ErrorCode[errMsg]
|
||||||
|
returnData := map[string]interface{}{}
|
||||||
|
|
||||||
|
// cookie, have_cookie := ctx.Get("cookie")
|
||||||
|
// if have_cookie {
|
||||||
|
// returnData["cookie"] = cookie
|
||||||
|
// }
|
||||||
|
|
||||||
|
returnData["err_code"] = errCode
|
||||||
|
returnData["err_msg"] = errMsg
|
||||||
|
if data != nil {
|
||||||
|
returnData["return"] = data
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.JSON(200, &returnData)
|
||||||
|
|
||||||
|
//ctx.Abort()
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func ReturnFile(ctx *gin.Context, file_info *models.TabFileInfo_, preview bool) {
|
||||||
|
if preview {
|
||||||
|
ctx.File(file_info.Path)
|
||||||
|
} else {
|
||||||
|
//需要从数据库拉取原始文件名
|
||||||
|
ctx.FileAttachment(file_info.Path, file_info.Name)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -1,20 +0,0 @@
|
|||||||
@echo off
|
|
||||||
echo Starting OPS Backend Development Server...
|
|
||||||
echo.
|
|
||||||
|
|
||||||
:: 设置Go环境变量
|
|
||||||
set CGO_ENABLED=1
|
|
||||||
|
|
||||||
:: 检查dist目录
|
|
||||||
if not exist ".\dist" mkdir dist
|
|
||||||
|
|
||||||
:: 运行开发服务器
|
|
||||||
echo Starting server with CGO enabled for SQLite...
|
|
||||||
echo Server will be available at: http://127.0.0.1:8080
|
|
||||||
echo.
|
|
||||||
|
|
||||||
go run ./cmd/ops-server/main.go
|
|
||||||
|
|
||||||
echo.
|
|
||||||
echo Server stopped.
|
|
||||||
pause
|
|
||||||
@@ -14,7 +14,7 @@ export default defineConfig({
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
build: {
|
build: {
|
||||||
outDir: "../../backend/dist",
|
outDir: "../../backend/my_work/dist",
|
||||||
assetsDir: "assets",
|
assetsDir: "assets",
|
||||||
},
|
},
|
||||||
server: {
|
server: {
|
||||||
|
|||||||
Reference in New Issue
Block a user