Skip to content

Commit aea3ba1

Browse files
authored
feat: add tag backup and fix bugs (#9265)
* feat(label): enhance label file binding and router setup (feat/add-tag-backup) - Add `GetLabelsByFileNamesPublic` to retrieve labels using file names. - Refactor router setup for label and file binding routes. - Improve `toObjsResp` for efficient label retrieval by file names. - Comment out unnecessary user ID parameter in `toObjsResp`. * feat(label): enhance label file binding and router setup - Add `GetLabelsByFileNamesPublic` for label retrieval by file names. - Refactor router setup for label and file binding routes. - Improve `toObjsResp` for efficient label retrieval by file names. - Comment out unnecessary user ID parameter in `toObjsResp`. * refactor(db): comment out debug print in GetLabelIds (#feat/add-tag-backup) - Comment out debug print statement in GetLabelIds to clean up logs. - Enhance code readability by removing unnecessary debug output. * feat(label-file-binding): add batch creation and improve label ID handling - Introduced `CreateLabelFileBinDingBatch` API for batch label binding. - Added `collectLabelIDs` helper function to handle label ID parsing. - Enhanced label ID handling to support varied delimiters and input formats. - Refactored `CreateLabelFileBinDing` logic for improved code readability. - Updated router to include `POST /label_file_binding/create_batch`.
1 parent 6b2d81e commit aea3ba1

File tree

9 files changed

+375
-34
lines changed

9 files changed

+375
-34
lines changed

internal/db/db.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ var db *gorm.DB
1212

1313
func Init(d *gorm.DB) {
1414
db = d
15-
err := AutoMigrate(new(model.Storage), new(model.User), new(model.Meta), new(model.SettingItem), new(model.SearchNode), new(model.TaskItem), new(model.SSHPublicKey), new(model.Role), new(model.Label), new(model.LabelFileBinDing), new(model.ObjFile))
15+
err := AutoMigrate(new(model.Storage), new(model.User), new(model.Meta), new(model.SettingItem), new(model.SearchNode), new(model.TaskItem), new(model.SSHPublicKey), new(model.Role), new(model.Label), new(model.LabelFileBinding), new(model.ObjFile))
1616
if err != nil {
1717
log.Fatalf("failed migrate database: %s", err.Error())
1818
}

internal/db/label_file_binding.go

Lines changed: 142 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,18 @@
11
package db
22

33
import (
4+
"fmt"
45
"github.com/alist-org/alist/v3/internal/model"
56
"github.com/pkg/errors"
67
"gorm.io/gorm"
8+
"gorm.io/gorm/clause"
79
"time"
810
)
911

1012
// GetLabelIds Get all label_ids from database order by file_name
1113
func GetLabelIds(userId uint, fileName string) ([]uint, error) {
12-
labelFileBinDingDB := db.Model(&model.LabelFileBinDing{})
14+
//fmt.Printf(">>> [GetLabelIds] userId: %d, fileName: %s\n", userId, fileName)
15+
labelFileBinDingDB := db.Model(&model.LabelFileBinding{})
1316
var labelIds []uint
1417
if err := labelFileBinDingDB.Where("file_name = ?", fileName).Where("user_id = ?", userId).Pluck("label_id", &labelIds).Error; err != nil {
1518
return nil, errors.WithStack(err)
@@ -18,7 +21,7 @@ func GetLabelIds(userId uint, fileName string) ([]uint, error) {
1821
}
1922

2023
func CreateLabelFileBinDing(fileName string, labelId, userId uint) error {
21-
var labelFileBinDing model.LabelFileBinDing
24+
var labelFileBinDing model.LabelFileBinding
2225
labelFileBinDing.UserId = userId
2326
labelFileBinDing.LabelId = labelId
2427
labelFileBinDing.FileName = fileName
@@ -32,25 +35,158 @@ func CreateLabelFileBinDing(fileName string, labelId, userId uint) error {
3235

3336
// GetLabelFileBinDingByLabelIdExists Get Label by label_id, used to del label usually
3437
func GetLabelFileBinDingByLabelIdExists(labelId, userId uint) bool {
35-
var labelFileBinDing model.LabelFileBinDing
38+
var labelFileBinDing model.LabelFileBinding
3639
result := db.Where("label_id = ?", labelId).Where("user_id = ?", userId).First(&labelFileBinDing)
3740
exists := !errors.Is(result.Error, gorm.ErrRecordNotFound)
3841
return exists
3942
}
4043

4144
// DelLabelFileBinDingByFileName used to del usually
4245
func DelLabelFileBinDingByFileName(userId uint, fileName string) error {
43-
return errors.WithStack(db.Where("file_name = ?", fileName).Where("user_id = ?", userId).Delete(model.LabelFileBinDing{}).Error)
46+
return errors.WithStack(db.Where("file_name = ?", fileName).Where("user_id = ?", userId).Delete(model.LabelFileBinding{}).Error)
4447
}
4548

4649
// DelLabelFileBinDingById used to del usually
4750
func DelLabelFileBinDingById(labelId, userId uint, fileName string) error {
48-
return errors.WithStack(db.Where("label_id = ?", labelId).Where("file_name = ?", fileName).Where("user_id = ?", userId).Delete(model.LabelFileBinDing{}).Error)
51+
return errors.WithStack(db.Where("label_id = ?", labelId).Where("file_name = ?", fileName).Where("user_id = ?", userId).Delete(model.LabelFileBinding{}).Error)
4952
}
5053

51-
func GetLabelFileBinDingByLabelId(labelIds []uint, userId uint) (result []model.LabelFileBinDing, err error) {
54+
func GetLabelFileBinDingByLabelId(labelIds []uint, userId uint) (result []model.LabelFileBinding, err error) {
5255
if err := db.Where("label_id in (?)", labelIds).Where("user_id = ?", userId).Find(&result).Error; err != nil {
5356
return nil, errors.WithStack(err)
5457
}
5558
return result, nil
5659
}
60+
61+
func GetLabelBindingsByFileNamesPublic(fileNames []string) (map[string][]uint, error) {
62+
var binds []model.LabelFileBinding
63+
if err := db.Where("file_name IN ?", fileNames).Find(&binds).Error; err != nil {
64+
return nil, errors.WithStack(err)
65+
}
66+
out := make(map[string][]uint, len(fileNames))
67+
seen := make(map[string]struct{}, len(binds))
68+
for _, b := range binds {
69+
key := fmt.Sprintf("%s-%d", b.FileName, b.LabelId)
70+
if _, ok := seen[key]; ok {
71+
continue
72+
}
73+
seen[key] = struct{}{}
74+
out[b.FileName] = append(out[b.FileName], b.LabelId)
75+
}
76+
return out, nil
77+
}
78+
79+
func GetLabelsByFileNamesPublic(fileNames []string) (map[string][]model.Label, error) {
80+
bindMap, err := GetLabelBindingsByFileNamesPublic(fileNames)
81+
if err != nil {
82+
return nil, err
83+
}
84+
85+
idSet := make(map[uint]struct{})
86+
for _, ids := range bindMap {
87+
for _, id := range ids {
88+
idSet[id] = struct{}{}
89+
}
90+
}
91+
if len(idSet) == 0 {
92+
return make(map[string][]model.Label, 0), nil
93+
}
94+
allIDs := make([]uint, 0, len(idSet))
95+
for id := range idSet {
96+
allIDs = append(allIDs, id)
97+
}
98+
labels, err := GetLabelByIds(allIDs) // 你已有的函数
99+
if err != nil {
100+
return nil, err
101+
}
102+
103+
labelByID := make(map[uint]model.Label, len(labels))
104+
for _, l := range labels {
105+
labelByID[l.ID] = l
106+
}
107+
108+
out := make(map[string][]model.Label, len(bindMap))
109+
for fname, ids := range bindMap {
110+
for _, id := range ids {
111+
if lab, ok := labelByID[id]; ok {
112+
out[fname] = append(out[fname], lab)
113+
}
114+
}
115+
}
116+
return out, nil
117+
}
118+
119+
func ListLabelFileBinDing(userId uint, labelIDs []uint, fileName string, page, pageSize int) ([]model.LabelFileBinding, int64, error) {
120+
q := db.Model(&model.LabelFileBinding{}).Where("user_id = ?", userId)
121+
122+
if len(labelIDs) > 0 {
123+
q = q.Where("label_id IN ?", labelIDs)
124+
}
125+
if fileName != "" {
126+
q = q.Where("file_name LIKE ?", "%"+fileName+"%")
127+
}
128+
129+
var total int64
130+
if err := q.Count(&total).Error; err != nil {
131+
return nil, 0, errors.WithStack(err)
132+
}
133+
134+
var rows []model.LabelFileBinding
135+
if err := q.
136+
Order("id DESC").
137+
Offset((page - 1) * pageSize).
138+
Limit(pageSize).
139+
Find(&rows).Error; err != nil {
140+
return nil, 0, errors.WithStack(err)
141+
}
142+
return rows, total, nil
143+
}
144+
145+
func RestoreLabelFileBindings(bindings []model.LabelFileBinding, keepIDs bool, override bool) error {
146+
if len(bindings) == 0 {
147+
return nil
148+
}
149+
tx := db.Begin()
150+
151+
if override {
152+
type key struct {
153+
uid uint
154+
name string
155+
}
156+
toDel := make(map[key]struct{}, len(bindings))
157+
for i := range bindings {
158+
k := key{uid: bindings[i].UserId, name: bindings[i].FileName}
159+
toDel[k] = struct{}{}
160+
}
161+
for k := range toDel {
162+
if err := tx.Where("user_id = ? AND file_name = ?", k.uid, k.name).
163+
Delete(&model.LabelFileBinding{}).Error; err != nil {
164+
tx.Rollback()
165+
return errors.WithStack(err)
166+
}
167+
}
168+
}
169+
170+
for i := range bindings {
171+
b := bindings[i]
172+
if !keepIDs {
173+
b.ID = 0
174+
}
175+
if b.CreateTime.IsZero() {
176+
b.CreateTime = time.Now()
177+
}
178+
if override {
179+
if err := tx.Create(&b).Error; err != nil {
180+
tx.Rollback()
181+
return errors.WithStack(err)
182+
}
183+
} else {
184+
if err := tx.Clauses(clause.OnConflict{DoNothing: true}).Create(&b).Error; err != nil {
185+
tx.Rollback()
186+
return errors.WithStack(err)
187+
}
188+
}
189+
}
190+
191+
return errors.WithStack(tx.Commit().Error)
192+
}

internal/errs/role.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,5 +3,5 @@ package errs
33
import "errors"
44

55
var (
6-
ErrChangeDefaultRole = errors.New("cannot modify admin or guest role")
6+
ErrChangeDefaultRole = errors.New("cannot modify admin role")
77
)

internal/model/label_file_binding.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ package model
22

33
import "time"
44

5-
type LabelFileBinDing struct {
5+
type LabelFileBinding struct {
66
ID uint `json:"id" gorm:"primaryKey"` // unique key
77
UserId uint `json:"user_id"` // use to user_id
88
LabelId uint `json:"label_id"` // use to label_id

internal/op/label_file_binding.go

Lines changed: 47 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ type CreateLabelFileBinDingReq struct {
2323
Type int `json:"type"`
2424
HashInfoStr string `json:"hashinfo"`
2525
LabelIds string `json:"label_ids"`
26+
LabelIDs []uint64 `json:"labelIdList"`
2627
}
2728

2829
type ObjLabelResp struct {
@@ -54,23 +55,29 @@ func GetLabelByFileName(userId uint, fileName string) ([]model.Label, error) {
5455
return labels, nil
5556
}
5657

58+
func GetLabelsByFileNamesPublic(fileNames []string) (map[string][]model.Label, error) {
59+
return db.GetLabelsByFileNamesPublic(fileNames)
60+
}
61+
5762
func CreateLabelFileBinDing(req CreateLabelFileBinDingReq, userId uint) error {
5863
if err := db.DelLabelFileBinDingByFileName(userId, req.Name); err != nil {
5964
return errors.WithMessage(err, "failed del label_file_bin_ding in database")
6065
}
61-
if req.LabelIds == "" {
66+
67+
ids, err := collectLabelIDs(req)
68+
if err != nil {
69+
return err
70+
}
71+
if len(ids) == 0 {
6272
return nil
6373
}
64-
labelMap := strings.Split(req.LabelIds, ",")
65-
for _, value := range labelMap {
66-
labelId, err := strconv.ParseUint(value, 10, 64)
67-
if err != nil {
68-
return fmt.Errorf("invalid label ID '%s': %v", value, err)
69-
}
70-
if err = db.CreateLabelFileBinDing(req.Name, uint(labelId), userId); err != nil {
74+
75+
for _, id := range ids {
76+
if err = db.CreateLabelFileBinDing(req.Name, uint(id), userId); err != nil {
7177
return errors.WithMessage(err, "failed labels in database")
7278
}
7379
}
80+
7481
if !db.GetFileByNameExists(req.Name) {
7582
objFile := model.ObjFile{
7683
Id: req.Id,
@@ -86,8 +93,7 @@ func CreateLabelFileBinDing(req CreateLabelFileBinDingReq, userId uint) error {
8693
Type: req.Type,
8794
HashInfoStr: req.HashInfoStr,
8895
}
89-
err := db.CreateObjFile(objFile)
90-
if err != nil {
96+
if err := db.CreateObjFile(objFile); err != nil {
9197
return errors.WithMessage(err, "failed file in database")
9298
}
9399
}
@@ -97,7 +103,7 @@ func CreateLabelFileBinDing(req CreateLabelFileBinDingReq, userId uint) error {
97103
func GetFileByLabel(userId uint, labelId string) (result []ObjLabelResp, err error) {
98104
labelMap := strings.Split(labelId, ",")
99105
var labelIds []uint
100-
var labelsFile []model.LabelFileBinDing
106+
var labelsFile []model.LabelFileBinding
101107
var labels []model.Label
102108
var labelsFileMap = make(map[string][]model.Label)
103109
var labelsMap = make(map[uint]model.Label)
@@ -157,3 +163,33 @@ func StringSliceToUintSlice(strSlice []string) ([]uint, error) {
157163
}
158164
return uintSlice, nil
159165
}
166+
167+
func RestoreLabelFileBindings(bindings []model.LabelFileBinding, keepIDs bool, override bool) error {
168+
return db.RestoreLabelFileBindings(bindings, keepIDs, override)
169+
}
170+
171+
func collectLabelIDs(req CreateLabelFileBinDingReq) ([]uint64, error) {
172+
if len(req.LabelIDs) > 0 {
173+
return req.LabelIDs, nil
174+
}
175+
s := strings.TrimSpace(req.LabelIds)
176+
if s == "" {
177+
return nil, nil
178+
}
179+
replacer := strings.NewReplacer(",", ",", "、", ",", ";", ",", ";", ",")
180+
s = replacer.Replace(s)
181+
parts := strings.Split(s, ",")
182+
ids := make([]uint64, 0, len(parts))
183+
for _, p := range parts {
184+
p = strings.TrimSpace(p)
185+
if p == "" {
186+
continue
187+
}
188+
id, err := strconv.ParseUint(p, 10, 64)
189+
if err != nil {
190+
return nil, fmt.Errorf("invalid label ID '%s': %v", p, err)
191+
}
192+
ids = append(ids, id)
193+
}
194+
return ids, nil
195+
}

server/handles/fsread.go

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,7 @@ func FsList(c *gin.Context) {
114114
provider = storage.GetStorage().Driver
115115
}
116116
common.SuccessResp(c, FsListResp{
117-
Content: toObjsResp(objs, reqPath, isEncrypt(meta, reqPath), user.ID),
117+
Content: toObjsResp(objs, reqPath, isEncrypt(meta, reqPath)),
118118
Total: int64(total),
119119
Readme: getReadme(meta, reqPath),
120120
Header: getHeader(meta, reqPath),
@@ -224,12 +224,22 @@ func pagination(objs []model.Obj, req *model.PageReq) (int, []model.Obj) {
224224
return total, objs[start:end]
225225
}
226226

227-
func toObjsResp(objs []model.Obj, parent string, encrypt bool, userId uint) []ObjLabelResp {
227+
func toObjsResp(objs []model.Obj, parent string, encrypt bool) []ObjLabelResp {
228228
var resp []ObjLabelResp
229+
230+
names := make([]string, 0, len(objs))
231+
for _, obj := range objs {
232+
if !obj.IsDir() {
233+
names = append(names, obj.GetName())
234+
}
235+
}
236+
237+
labelsByName, _ := op.GetLabelsByFileNamesPublic(names)
238+
229239
for _, obj := range objs {
230240
var labels []model.Label
231-
if obj.IsDir() == false {
232-
labels, _ = op.GetLabelByFileName(userId, obj.GetName())
241+
if !obj.IsDir() {
242+
labels = labelsByName[obj.GetName()]
233243
}
234244
thumb, _ := model.GetThumb(obj)
235245
resp = append(resp, ObjLabelResp{
@@ -369,7 +379,7 @@ func FsGet(c *gin.Context) {
369379
Readme: getReadme(meta, reqPath),
370380
Header: getHeader(meta, reqPath),
371381
Provider: provider,
372-
Related: toObjsResp(related, parentPath, isEncrypt(parentMeta, parentPath), user.ID),
382+
Related: toObjsResp(related, parentPath, isEncrypt(parentMeta, parentPath)),
373383
})
374384
}
375385

0 commit comments

Comments
 (0)