Skip to content

Commit

Permalink
♻️ Migrate from gorm/sqlite to boltdb
Browse files Browse the repository at this point in the history
  • Loading branch information
nitezs committed Apr 24, 2024
1 parent 3d3b4e0 commit 566965b
Show file tree
Hide file tree
Showing 12 changed files with 176 additions and 257 deletions.
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,6 @@
dist
subs
logs
sub2clash.db
data
.env
.vscode/settings.json
48 changes: 27 additions & 21 deletions .goreleaser.yaml
Original file line number Diff line number Diff line change
@@ -1,24 +1,30 @@
project_name: sub2clash
builds:
- env:
- CGO_ENABLED=0
goos:
- windows
- linux
- darwin
goarch:
- amd64
ldflags:
- -s -w -X sub2clash/constant.Version={{ .Version }}
flags:
- -trimpath
- env:
- CGO_ENABLED=0
goos:
- windows
- linux
- darwin
goarch:
- amd64
- arm64
- arm
- "386"
goarm:
- "6"
- "7"
ldflags:
- -s -w -X sub2clash/constant.Version={{ .Version }}
flags:
- -trimpath
archives:
- format: tar.gz
format_overrides:
- format: zip
goos: windows
wrap_in_directory: true
files:
- LICENSE
- README.md
- templates
- format: tar.gz
format_overrides:
- format: zip
goos: windows
wrap_in_directory: true
files:
- LICENSE
- README.md
- templates
2 changes: 1 addition & 1 deletion .vscode/launch.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
"mode": "debug",
"program": "${workspaceFolder}/main.go",
"output": "${workspaceFolder}/dist/main.exe",
"buildFlags": "-ldflags '-X sub2clash/config.Version=dev'"
"buildFlags": "-ldflags '-X sub2clash/constant.Version=dev'"
}
]
}
179 changes: 78 additions & 101 deletions api/handler/short_link.go
Original file line number Diff line number Diff line change
@@ -1,160 +1,137 @@
package handler

import (
"errors"
"io"
"net/http"
"strconv"
"strings"
"time"

"sub2clash/common"
"sub2clash/common/database"
"sub2clash/config"
"sub2clash/logger"
"sub2clash/model"
"sub2clash/validator"
"time"

"github.com/gin-gonic/gin"
"go.uber.org/zap"
"gorm.io/gorm"
)

func ShortLinkGenHandler(c *gin.Context) {
// 从请求中获取参数
func respondWithError(c *gin.Context, code int, message string) {
c.String(code, message)
c.Abort()
}

func GenerateLinkHandler(c *gin.Context) {
var params validator.ShortLinkGenValidator
if err := c.ShouldBind(&params); err != nil {
c.String(400, "参数错误: "+err.Error())
respondWithError(c, http.StatusBadRequest, "参数错误: "+err.Error())
return
}
if strings.TrimSpace(params.Url) == "" {
c.String(400, "参数错误")
respondWithError(c, http.StatusBadRequest, "URL 不能为空")
return
}
// 生成hash
hash := common.RandomString(config.Default.ShortLinkLength)
var item model.ShortLink
result := database.FindShortLinkByUrl(params.Url, &item)
if result.Error == nil {
if item.Password != params.Password {
item.Password = params.Password
database.SaveShortLink(&item)
c.String(200, item.Hash+"?password="+params.Password)
} else {
c.String(200, item.Hash)
}

hash, err := generateUniqueHash()
if err != nil {
respondWithError(c, http.StatusInternalServerError, "生成短链接失败")
return
}

shortLink := model.ShortLink{
Hash: hash,
Url: params.Url,
Password: params.Password,
}

if err := database.SaveShortLink(&shortLink); err != nil {
respondWithError(c, http.StatusInternalServerError, "数据库错误")
return
} else {
if !errors.Is(result.Error, gorm.ErrRecordNotFound) {
c.String(500, "数据库错误: "+result.Error.Error())
return
}
}
// 如果记录存在则重新生成hash,直到记录不存在
result = database.FindShortLinkByHash(hash, &item)
for result.Error == nil {
hash = common.RandomString(config.Default.ShortLinkLength)
result = database.FindShortLinkByHash(hash, &item)
}
// 创建记录
database.FirstOrCreateShortLink(
&model.ShortLink{
Hash: hash,
Url: params.Url,
LastRequestTime: -1,
Password: params.Password,
},
)
// 返回短链接

if params.Password != "" {
hash += "?password=" + params.Password
}
c.String(200, hash)
c.String(http.StatusOK, hash)
}

func ShortLinkGetUrlHandler(c *gin.Context) {
var params validator.ShortLinkGetValidator
if err := c.ShouldBindQuery(&params); err != nil {
c.String(400, "参数错误: "+err.Error())
return
func generateUniqueHash() (string, error) {
for {
hash := common.RandomString(config.Default.ShortLinkLength)
exists, err := database.CheckShortLinkHashExists(hash)
if err != nil {
return "", err
}
if !exists {
return hash, nil
}
}
if strings.TrimSpace(params.Hash) == "" {
c.String(400, "参数错误")
}

func UpdateLinkHandler(c *gin.Context) {
var params validator.ShortLinkUpdateValidator
if err := c.ShouldBindJSON(&params); err != nil {
respondWithError(c, http.StatusBadRequest, "参数错误: "+err.Error())
return
}
var shortLink model.ShortLink
result := database.FindShortLinkByHash(params.Hash, &shortLink)
if result.Error != nil {
c.String(404, "未找到短链接")
return
shortLink := model.ShortLink{
Hash: params.Hash,
Url: params.Url,
Password: params.Password,
}
if shortLink.Password != "" && shortLink.Password != params.Password {
c.String(403, "密码错误")
if err := database.SaveShortLink(&shortLink); err != nil {
respondWithError(c, http.StatusInternalServerError, "数据库错误")
return
}
c.String(200, shortLink.Url)

c.String(http.StatusOK, "短链接更新成功")
}

func ShortLinkGetConfigHandler(c *gin.Context) {
// 获取动态路由
func GetRawConfHandler(c *gin.Context) {
// 获取动态路由参数
hash := c.Param("hash")
password := c.Query("password")

if strings.TrimSpace(hash) == "" {
c.String(400, "参数错误")
c.String(http.StatusBadRequest, "参数错误")
return
}
// 查询数据库
var shortLink model.ShortLink
result := database.FindShortLinkByHash(hash, &shortLink)
// 重定向
if result.Error != nil {
c.String(404, "未找到短链接或密码错误")

// 查询数据库中的短链接
shortLink, err := database.FindShortLinkByHash(hash)
if err != nil {
c.String(http.StatusNotFound, "未找到短链接或密码错误")
return
}

// 校验密码
if shortLink.Password != "" && shortLink.Password != password {
c.String(404, "未找到短链接或密码错误")
c.String(http.StatusNotFound, "未找到短链接或密码错误")
return
}

// 更新最后访问时间
shortLink.LastRequestTime = time.Now().Unix()
database.SaveShortLink(&shortLink)
get, err := common.Get("http://localhost:" + strconv.Itoa(config.Default.Port) + "/" + shortLink.Url)
err = database.SaveShortLink(shortLink)
if err != nil {
logger.Logger.Debug("get short link data failed", zap.Error(err))
c.String(500, "请求错误: "+err.Error())
respondWithError(c, http.StatusInternalServerError, "数据库错误")
return
}
all, err := io.ReadAll(get.Body)
// 请求短链接指向的URL
response, err := http.Get("http://localhost:" + strconv.Itoa(config.Default.Port) + "/" + shortLink.Url)
if err != nil {
logger.Logger.Debug("read short link data failed", zap.Error(err))
c.String(500, "读取错误: "+err.Error())
respondWithError(c, http.StatusInternalServerError, "请求错误: "+err.Error())
return
}
c.String(http.StatusOK, string(all))
}
defer response.Body.Close()

func ShortLinkUpdateHandler(c *gin.Context) {
var params validator.ShortLinkUpdateValidator
if err := c.ShouldBind(&params); err != nil {
c.String(400, "参数错误: "+err.Error())
}
if strings.TrimSpace(params.Url) == "" {
c.String(400, "参数错误")
return
}
var shortLink model.ShortLink
result := database.FindShortLinkByHash(params.Hash, &shortLink)
if result.Error != nil {
c.String(404, "未找到短链接")
return
}
if shortLink.Password == "" {
c.String(403, "无法修改无密码短链接")
return
}
if shortLink.Password != params.Password {
c.String(403, "密码错误")
// 读取响应内容
all, err := io.ReadAll(response.Body)
if err != nil {
respondWithError(c, http.StatusInternalServerError, "读取错误: "+err.Error())
return
}
shortLink.Url = params.Url
database.SaveShortLink(&shortLink)
c.String(200, "更新成功")

// 返回响应内容
c.String(http.StatusOK, string(all))
}
34 changes: 5 additions & 29 deletions api/route.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,33 +43,9 @@ func SetRoute(r *gin.Engine) {
)
},
)
r.GET(
"/clash", func(c *gin.Context) {
handler.SubmodHandler(c)
},
)
r.GET(
"/meta", func(c *gin.Context) {
handler.SubHandler(c)
},
)
r.GET(
"/s/:hash", func(c *gin.Context) {
handler.ShortLinkGetConfigHandler(c)
},
)
r.GET(
"/short", func(c *gin.Context) {
handler.ShortLinkGetUrlHandler(c)
})
r.POST(
"/short", func(c *gin.Context) {
handler.ShortLinkGenHandler(c)
},
)
r.PUT(
"/short", func(c *gin.Context) {
handler.ShortLinkUpdateHandler(c)
},
)
r.GET("/clash", handler.SubmodHandler)
r.GET("/meta", handler.SubHandler)
r.GET("/s/:hash", handler.GetRawConfHandler)
r.POST("/short", handler.GenerateLinkHandler)
r.PUT("/short", handler.UpdateLinkHandler)
}
Loading

0 comments on commit 566965b

Please sign in to comment.