Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 13 additions & 1 deletion DockerFile
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,24 @@ RUN go get github.com/akrylysov/algnhsa && \
go get github.com/sirupsen/logrus && \
go build ./main.go

FROM openeuler/openeuler:22.03
FROM openeuler/openeuler:24.03
LABEL maintainer="Zhou Yi 1123678689@qq.com"

# 安装依赖工具
RUN dnf install -y git wget tar gzip && \
# 下载git-lfs
wget https://github.com/git-lfs/git-lfs/releases/download/v3.3.0/git-lfs-linux-amd64-v3.3.0.tar.gz && \
tar -xzf git-lfs-linux-amd64-v3.3.0.tar.gz && \
cd git-lfs-3.3.0 && \
./install.sh && \
rm -rf git-lfs-* && \
dnf clean all

RUN useradd -s /bin/bash BigFiles
USER BigFiles
WORKDIR /home/BigFiles
COPY --chown=BigFiles:group --from=BUILDER /home/main /home/BigFiles/main
COPY --chown=BigFiles:group --from=BUILDER /home/scripts/lfsNameQuery.py /home/BigFiles/lfsNameQuery.py

EXPOSE 5000
ENTRYPOINT ["/home/BigFiles/main"]
131 changes: 124 additions & 7 deletions auth/gitee.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,10 @@ import (
"net/http"
"net/url"
"os"
"os/exec"
"path/filepath"
"strings"
"time"

"github.com/metalogical/BigFiles/batch"
"github.com/metalogical/BigFiles/config"
Expand Down Expand Up @@ -139,7 +142,7 @@ func GiteeAuth() func(UserInRepo) error {
}
}

if err := CheckRepoOwner(userInRepo); err != nil {
if _, err := CheckRepoOwner(userInRepo); err != nil {
return err
}

Expand All @@ -148,7 +151,7 @@ func GiteeAuth() func(UserInRepo) error {
}

// CheckRepoOwner checks whether the owner of a repo is allowed to use lfs server
func CheckRepoOwner(userInRepo UserInRepo) error {
func CheckRepoOwner(userInRepo UserInRepo) (Repo, error) {
path := fmt.Sprintf(
"https://gitee.com/api/v5/repos/%s/%s%s",
userInRepo.Owner,
Expand All @@ -165,24 +168,24 @@ func CheckRepoOwner(userInRepo UserInRepo) error {
err := getParsedResponse("GET", path, headers, nil, &repo)
if err != nil {
msg := err.Error() + ": check repo_id failed"
return errors.New(msg)
return *repo, errors.New(msg)
}
for _, allowedRepo := range allowedRepos {
if strings.Split(repo.Fullname, "/")[0] == allowedRepo {
return nil
return *repo, nil
}
}

if repo.Parent.Fullname != "" {
for _, allowedRepo := range allowedRepos {
if strings.Split(repo.Parent.Fullname, "/")[0] == allowedRepo {
return nil
return *repo, nil
}
}
}
msg := "forbidden: repo has no permission to use this lfs server"
logrus.Error(fmt.Sprintf("CheckRepoOwner | %s", msg))
return errors.New(msg)
return *repo, errors.New(msg)
}

// getToken gets access_token by username and password
Expand Down Expand Up @@ -242,7 +245,7 @@ func VerifyUser(userInRepo UserInRepo) error {
} else if userInRepo.Operation == "delete" {
return verifyUserDelete(giteeUser, userInRepo)
} else {
msg := "system_error: unknow operation"
msg := "system_error: unknown operation"
logrus.Error(fmt.Sprintf(formatLogString, verifyLog, msg))
return errors.New(msg)
}
Expand Down Expand Up @@ -385,6 +388,120 @@ func GetAccountManageToken() (string, error) {
return managerTokenOutput.Token, err
}

// FileInfo 包含LFS文件的名称和大小信息
type FileInfo struct {
Name string `json:"name"`
Size int64 `json:"size"`
}

// GetLFSMapping 调用Python脚本获取LFS文件映射
// 参数:
// - userInRepo: 仓库相关信息
// - pythonScriptPath: Python脚本路径(可选)
//
// 返回:
// - map[string]FileInfo: OID到文件信息的映射
// - error: 错误信息
func GetLFSMapping(userInRepo UserInRepo, pythonScriptPath ...string) (map[string]FileInfo, error) {
owner := userInRepo.Owner
repo := userInRepo.Repo
username := userInRepo.Username
token := userInRepo.Token

// 确定Python脚本路径
scriptPath, err := resolveScriptPath(pythonScriptPath...)
if err != nil {
return nil, err
}

// 创建临时文件
outputFile, cleanup, err := createTempOutputFile()
if err != nil {
return nil, err
}
defer cleanup()

// 构建并执行命令
cmd := exec.Command("python3")
cmd.Args = append(cmd.Args, scriptPath)
cmd.Args = append(cmd.Args, owner)
cmd.Args = append(cmd.Args, repo)
cmd.Args = append(cmd.Args, outputFile)
cmd.Args = append(cmd.Args, username)
cmd.Args = append(cmd.Args, token)
cmd.Stderr = os.Stderr

// 运行命令
if err := cmd.Run(); err != nil {
var exitErr *exec.ExitError
if errors.As(err, &exitErr) {
return nil, fmt.Errorf("Python脚本执行失败,退出码%d: %w", exitErr.ExitCode(), err)
}
return nil, fmt.Errorf("执行Python脚本出错: %w", err)
}

// 读取并解析结果
return parseOutputFile(outputFile)
}

// 解析脚本路径
func resolveScriptPath(pythonScriptPath ...string) (string, error) {
if len(pythonScriptPath) > 0 {
return pythonScriptPath[0], nil
}

exePath, err := os.Executable()
if err != nil {
return "", fmt.Errorf("获取可执行文件路径失败: %w", err)
}

scriptPath := filepath.Join(filepath.Dir(exePath), "lfsNameQuery.py")
if _, err := os.Stat(scriptPath); os.IsNotExist(err) {
return "", fmt.Errorf("Python脚本不存在于: %s", scriptPath)
}

return scriptPath, nil
}

// 创建临时输出文件
func createTempOutputFile() (string, func(), error) {
tempDir := os.TempDir()
outputFile := filepath.Join(tempDir, fmt.Sprintf("lfs_mapping_%d.json", time.Now().UnixNano()))
cleanup := func() {
err := os.Remove(outputFile)
if err != nil {
logrus.Warnf("Failed to remove temporary output file %s", outputFile)
return
}
}
return outputFile, cleanup, nil
}

// 解析输出文件
func parseOutputFile(outputFile string) (map[string]FileInfo, error) {
// 转换为绝对路径
absPath, err := filepath.Abs(outputFile)
safeDir := "/tmp"
// 检查路径是否在安全目录内
if !strings.HasPrefix(filepath.Clean(absPath), safeDir) {
return nil, fmt.Errorf("access denied: file must be in %s", safeDir)
}
if err != nil {
return nil, fmt.Errorf("invalid file path: %w", err)
}
data, err := os.ReadFile(absPath)
if err != nil {
return nil, fmt.Errorf("读取输出文件失败: %w", err)
}

var mapping map[string]FileInfo
if err := json.Unmarshal(data, &mapping); err != nil {
return nil, fmt.Errorf("解析JSON失败: %w", err)
}

return mapping, nil
}

func generateError(err error, m string) error {
msg := err.Error() + m
logrus.Error(fmt.Sprintf(formatLogString, verifyLog, msg))
Expand Down
4 changes: 2 additions & 2 deletions auth/gitee_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ func (s *SuiteGitee) TestCheckRepoOwner() {
Owner: s.Owner,
Token: s.cfg.DefaultToken,
}
err := CheckRepoOwner(userInRepo)
_, err := CheckRepoOwner(userInRepo)
assert.NotNil(s.T(), err)

// check no_exist repo
Expand All @@ -80,7 +80,7 @@ func (s *SuiteGitee) TestCheckRepoOwner() {
Owner: "owner",
Token: s.cfg.DefaultToken,
}
err = CheckRepoOwner(userInRepo)
_, err = CheckRepoOwner(userInRepo)
assert.NotNil(s.T(), err)
}

Expand Down
7 changes: 4 additions & 3 deletions config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,10 @@ type Config struct {

type ValidateConfig struct {
OwnerRegexp string `json:"OWNER_REGEXP" required:"true"`
RepoNameRegexp string `json:"REPONAME_REGEXP" required:"true"`
UsernameRegexp string `json:"USERNAME_REGEXP" required:"true"`
PasswordRegexp string `json:"PASSWORD_REGEXP" required:"true"`
RepoNameRegexp string `json:"REPONAME_REGEXP" required:"true"`
UsernameRegexp string `json:"USERNAME_REGEXP" required:"true"`
PasswordRegexp string `json:"PASSWORD_REGEXP" required:"true"`
WebhookKey string `json:"WEBHOOK_KEY" required:"true"`
}

type DBConfig struct {
Expand Down
74 changes: 71 additions & 3 deletions db/db.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package db

import (
"errors"
"fmt"
"log"
"time"
Expand All @@ -13,7 +14,8 @@ import (
)

var (
Db *gorm.DB
Db *gorm.DB
oidHolder = "oid = ?"
)

// Init initializes the database connection and configuration.
Expand Down Expand Up @@ -52,6 +54,7 @@ func DB() *gorm.DB {
type LfsObj struct {
ID int `gorm:"primaryKey;autoIncrement;comment:'自增ID'"`
Oid string `gorm:"size:511;not null;default:'';index:idx_oid;comment:'文件OID'"`
FileName string `gorm:"size:255;default:'';comment:'文件名'"`
Size int `gorm:"not null;comment:'文件大小'"`
Platform string `gorm:"size:64;not null;default:'gitee';index:idx_platform;comment:'所属平台,默认为gitee'"`
Owner string `gorm:"size:100;not null;index:idx_platform;comment:'仓库owner'"`
Expand Down Expand Up @@ -96,7 +99,7 @@ func DeleteLFSObj(obj LfsObj) error {
// CountLFSObj 查找指定 OID 的 LFS 元数据数量
func CountLFSObj(obj LfsObj) (int64, error) {
var count int64
result := Db.Model(&LfsObj{}).Where("oid = ?", obj.Oid).Count(&count)
result := Db.Model(&LfsObj{}).Where(oidHolder, obj.Oid).Count(&count)
if result.Error != nil {
return 0, fmt.Errorf("failed to count LFS objects: %w", result.Error)
}
Expand All @@ -116,8 +119,73 @@ func GetUploadLfsObj() ([]LfsObj, error) {
// SelectLfsObjByOid 通过OID查找指定了LFS数据
func SelectLfsObjByOid(oid string) ([]LfsObj, error) {
var result []LfsObj
if err := Db.Where("oid = ?", oid).Find(&result).Error; err != nil {
if err := Db.Where(oidHolder, oid).Find(&result).Error; err != nil {
return nil, fmt.Errorf("failed to get LfsObj: %w", err)
}
return result, nil
}

// UpdateLFSObjFileName 更新LFS对象的文件名
// 参数:
// - oid: 文件OID
// - newFileName: 新文件名
// - operator: 操作人
//
// 返回:
// - error: 错误信息
func UpdateLFSObjFileName(oid, newFileName, operator string) error {
// 参数校验
if oid == "" {
return fmt.Errorf("OID不能为空")
}
if newFileName == "" {
return fmt.Errorf("新文件名不能为空")
}

// 开启事务
tx := Db.Begin()
defer func() {
if r := recover(); r != nil {
tx.Rollback()
}
}()

// 查询现有记录
var obj LfsObj
if err := tx.Where(oidHolder, oid).First(&obj).Error; err != nil {
tx.Rollback()
if errors.Is(err, gorm.ErrRecordNotFound) {
return fmt.Errorf("未找到OID为 %s 的记录", oid)
}
return fmt.Errorf("查询失败: %w", err)
}

// 检查是否需要更新
if obj.FileName == newFileName {
tx.Rollback()
return nil // 文件名相同,无需更新
}

// 执行更新
updateData := map[string]interface{}{
"file_name": newFileName,
"operator": operator,
"update_time": time.Now().Add(8 * time.Hour),
}

if err := tx.Model(&LfsObj{}).
Where(oidHolder, oid).
Updates(updateData).Error; err != nil {
tx.Rollback()
return fmt.Errorf("更新失败: %w", err)
}

// 提交事务
if err := tx.Commit().Error; err != nil {
return fmt.Errorf("提交事务失败: %w", err)
}

log.Printf("成功更新文件OID: %s, 旧文件名: %s, 新文件名: %s",
oid, obj.FileName, newFileName)
return nil
}
1 change: 1 addition & 0 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,7 @@ func main() {
})

go server.StartScheduledTask()
go server.ScheduledCheckOidAndFileName()

srv := &http.Server{
Addr: "0.0.0.0:5000",
Expand Down
Loading
Loading