mirror of
				https://github.com/cloudreve/cloudreve.git
				synced 2025-10-31 08:39:10 +08:00 
			
		
		
		
	Test: thumbnail and authn / Modify: read thumbnail config from file
This commit is contained in:
		| @ -2,6 +2,11 @@ | ||||
| Debug = true | ||||
| SessionSecret = 23333 | ||||
|  | ||||
| [Thumbnail] | ||||
| MaxWidth = 400 | ||||
| MaxHeight = 300 | ||||
| FileSuffix = ._thumb | ||||
|  | ||||
| [Database] | ||||
| Type = mysql | ||||
| User = root | ||||
|  | ||||
| @ -2,12 +2,9 @@ package model | ||||
|  | ||||
| import ( | ||||
| 	"crypto/sha1" | ||||
| 	"encoding/binary" | ||||
| 	"encoding/hex" | ||||
| 	"encoding/json" | ||||
| 	"fmt" | ||||
| 	"github.com/HFO4/cloudreve/pkg/util" | ||||
| 	"github.com/duo-labs/webauthn/webauthn" | ||||
| 	"github.com/jinzhu/gorm" | ||||
| 	"github.com/pkg/errors" | ||||
| 	"strings" | ||||
| @ -59,41 +56,6 @@ type UserOption struct { | ||||
| 	PreferredTheme  string `json:"preferred_theme"` | ||||
| } | ||||
|  | ||||
| func (user User) WebAuthnID() []byte { | ||||
| 	bs := make([]byte, 8) | ||||
| 	binary.LittleEndian.PutUint64(bs, uint64(user.ID)) | ||||
| 	return bs | ||||
| } | ||||
|  | ||||
| func (user User) WebAuthnName() string { | ||||
| 	return user.Email | ||||
| } | ||||
|  | ||||
| func (user User) WebAuthnDisplayName() string { | ||||
| 	return user.Nick | ||||
| } | ||||
|  | ||||
| func (user User) WebAuthnIcon() string { | ||||
| 	return "https://cdn4.buysellads.net/uu/1/46074/1559075156-slack-carbon-red_2x.png" | ||||
| } | ||||
|  | ||||
| func (user User) WebAuthnCredentials() []webauthn.Credential { | ||||
| 	var res []webauthn.Credential | ||||
| 	err := json.Unmarshal([]byte(user.Authn), &res) | ||||
| 	if err != nil { | ||||
| 		fmt.Println(err) | ||||
| 	} | ||||
| 	return res | ||||
| } | ||||
|  | ||||
| func (user *User) RegisterAuthn(credential *webauthn.Credential) { | ||||
| 	res, err := json.Marshal([]webauthn.Credential{*credential}) | ||||
| 	if err != nil { | ||||
| 		fmt.Println(err) | ||||
| 	} | ||||
| 	DB.Model(user).UpdateColumn("authn", string(res)) | ||||
| } | ||||
|  | ||||
| // Root 获取用户的根目录 | ||||
| func (user *User) Root() (*Folder, error) { | ||||
| 	var folder Folder | ||||
| @ -157,18 +119,17 @@ func (user *User) GetPolicyID() uint { | ||||
| 			return user.Group.PolicyList[0] | ||||
| 		} | ||||
| 		return 1 | ||||
| 	} else { | ||||
| 		// 用户指定时,先检查是否为可用策略列表中的值 | ||||
| 		if util.ContainsUint(user.Group.PolicyList, user.OptionsSerialized.PreferredPolicy) { | ||||
| 			return user.OptionsSerialized.PreferredPolicy | ||||
| 		} | ||||
| 		// 不可用时,返回第一个 | ||||
| 		if len(user.Group.PolicyList) != 0 { | ||||
| 			return user.Group.PolicyList[0] | ||||
| 		} | ||||
| 		return 1 | ||||
|  | ||||
| 	} | ||||
| 	// 用户指定时,先检查是否为可用策略列表中的值 | ||||
| 	if util.ContainsUint(user.Group.PolicyList, user.OptionsSerialized.PreferredPolicy) { | ||||
| 		return user.OptionsSerialized.PreferredPolicy | ||||
| 	} | ||||
| 	// 不可用时,返回第一个 | ||||
| 	if len(user.Group.PolicyList) != 0 { | ||||
| 		return user.Group.PolicyList[0] | ||||
| 	} | ||||
| 	return 1 | ||||
|  | ||||
| } | ||||
|  | ||||
| // GetUserByID 用ID获取用户 | ||||
|  | ||||
							
								
								
									
										53
									
								
								models/user_authn.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										53
									
								
								models/user_authn.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,53 @@ | ||||
| package model | ||||
|  | ||||
| import ( | ||||
| 	"encoding/binary" | ||||
| 	"encoding/json" | ||||
| 	"fmt" | ||||
| 	"github.com/duo-labs/webauthn/webauthn" | ||||
| ) | ||||
|  | ||||
| /* | ||||
| 	`webauthn.User` 接口的实现 | ||||
| */ | ||||
|  | ||||
| // WebAuthnID 返回用户ID | ||||
| func (user User) WebAuthnID() []byte { | ||||
| 	bs := make([]byte, 8) | ||||
| 	binary.LittleEndian.PutUint64(bs, uint64(user.ID)) | ||||
| 	return bs | ||||
| } | ||||
|  | ||||
| // WebAuthnName 返回用户名 | ||||
| func (user User) WebAuthnName() string { | ||||
| 	return user.Email | ||||
| } | ||||
|  | ||||
| // WebAuthnDisplayName 获得用于展示的用户名 | ||||
| func (user User) WebAuthnDisplayName() string { | ||||
| 	return user.Nick | ||||
| } | ||||
|  | ||||
| // WebAuthnIcon 获得用户头像 | ||||
| func (user User) WebAuthnIcon() string { | ||||
| 	return "https://cdn4.buysellads.net/uu/1/46074/1559075156-slack-carbon-red_2x.png" | ||||
| } | ||||
|  | ||||
| // WebAuthnCredentials 获得已注册的验证器凭证 | ||||
| func (user User) WebAuthnCredentials() []webauthn.Credential { | ||||
| 	var res []webauthn.Credential | ||||
| 	err := json.Unmarshal([]byte(user.Authn), &res) | ||||
| 	if err != nil { | ||||
| 		fmt.Println(err) | ||||
| 	} | ||||
| 	return res | ||||
| } | ||||
|  | ||||
| // RegisterAuthn 添加新的验证器 | ||||
| func (user *User) RegisterAuthn(credential *webauthn.Credential) { | ||||
| 	res, err := json.Marshal([]webauthn.Credential{*credential}) | ||||
| 	if err != nil { | ||||
| 		fmt.Println(err) | ||||
| 	} | ||||
| 	DB.Model(user).UpdateColumn("authn", string(res)) | ||||
| } | ||||
							
								
								
									
										84
									
								
								models/user_authn_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										84
									
								
								models/user_authn_test.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,84 @@ | ||||
| package model | ||||
|  | ||||
| import ( | ||||
| 	"github.com/DATA-DOG/go-sqlmock" | ||||
| 	"github.com/duo-labs/webauthn/webauthn" | ||||
| 	"github.com/jinzhu/gorm" | ||||
| 	"github.com/stretchr/testify/assert" | ||||
| 	"testing" | ||||
| ) | ||||
|  | ||||
| func TestUser_RegisterAuthn(t *testing.T) { | ||||
| 	asserts := assert.New(t) | ||||
| 	credential := webauthn.Credential{} | ||||
| 	user := User{ | ||||
| 		Model: gorm.Model{ID: 1}, | ||||
| 	} | ||||
|  | ||||
| 	{ | ||||
| 		mock.ExpectBegin() | ||||
| 		mock.ExpectExec("UPDATE(.+)"). | ||||
| 			WillReturnResult(sqlmock.NewResult(1, 1)) | ||||
| 		mock.ExpectCommit() | ||||
| 		user.RegisterAuthn(&credential) | ||||
| 		asserts.NoError(mock.ExpectationsWereMet()) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func TestUser_WebAuthnCredentials(t *testing.T) { | ||||
| 	asserts := assert.New(t) | ||||
| 	user := User{ | ||||
| 		Model: gorm.Model{ID: 1}, | ||||
| 		Authn: `[{"ID":"123","PublicKey":"+4sg1vYcjg/+=","AttestationType":"packed","Authenticator":{"AAGUID":"+lg==","SignCount":0,"CloneWarning":false}}]`, | ||||
| 	} | ||||
| 	{ | ||||
| 		credentials := user.WebAuthnCredentials() | ||||
| 		asserts.Len(credentials, 1) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func TestUser_WebAuthnDisplayName(t *testing.T) { | ||||
| 	asserts := assert.New(t) | ||||
| 	user := User{ | ||||
| 		Model: gorm.Model{ID: 1}, | ||||
| 		Nick:  "123", | ||||
| 	} | ||||
| 	{ | ||||
| 		nick := user.WebAuthnDisplayName() | ||||
| 		asserts.Equal("123", nick) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func TestUser_WebAuthnIcon(t *testing.T) { | ||||
| 	asserts := assert.New(t) | ||||
| 	user := User{ | ||||
| 		Model: gorm.Model{ID: 1}, | ||||
| 	} | ||||
| 	{ | ||||
| 		icon := user.WebAuthnIcon() | ||||
| 		asserts.NotEmpty(icon) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func TestUser_WebAuthnID(t *testing.T) { | ||||
| 	asserts := assert.New(t) | ||||
| 	user := User{ | ||||
| 		Model: gorm.Model{ID: 1}, | ||||
| 	} | ||||
| 	{ | ||||
| 		id := user.WebAuthnID() | ||||
| 		asserts.Len(id, 8) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func TestUser_WebAuthnName(t *testing.T) { | ||||
| 	asserts := assert.New(t) | ||||
| 	user := User{ | ||||
| 		Model: gorm.Model{ID: 1}, | ||||
| 		Email: "abslant@foxmail.com", | ||||
| 	} | ||||
| 	{ | ||||
| 		name := user.WebAuthnName() | ||||
| 		asserts.Equal("abslant@foxmail.com", name) | ||||
| 	} | ||||
| } | ||||
| @ -5,11 +5,11 @@ import ( | ||||
| 	"github.com/duo-labs/webauthn/webauthn" | ||||
| ) | ||||
|  | ||||
| var Authn *webauthn.WebAuthn | ||||
| var AuthnInstance *webauthn.WebAuthn | ||||
|  | ||||
| func Init() { | ||||
| 	var err error | ||||
| 	Authn, err = webauthn.New(&webauthn.Config{ | ||||
| 	AuthnInstance, err = webauthn.New(&webauthn.Config{ | ||||
| 		RPDisplayName: "Duo Labs",                 // Display Name for your site | ||||
| 		RPID:          "localhost",                // Generally the FQDN for your site | ||||
| 		RPOrigin:      "http://localhost:3000",    // The origin URL for WebAuthn requests | ||||
|  | ||||
							
								
								
									
										15
									
								
								pkg/authn/auth_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								pkg/authn/auth_test.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,15 @@ | ||||
| package authn | ||||
|  | ||||
| import ( | ||||
| 	"github.com/stretchr/testify/assert" | ||||
| 	"testing" | ||||
| ) | ||||
|  | ||||
| func TestInit(t *testing.T) { | ||||
| 	asserts := assert.New(t) | ||||
|  | ||||
| 	asserts.NotPanics(func() { | ||||
| 		Init() | ||||
| 	}) | ||||
| 	asserts.NotNil(AuthnInstance) | ||||
| } | ||||
| @ -3,7 +3,6 @@ package conf | ||||
| import ( | ||||
| 	"github.com/HFO4/cloudreve/pkg/util" | ||||
| 	"github.com/go-ini/ini" | ||||
| 	"github.com/mojocn/base64Captcha" | ||||
| 	"gopkg.in/go-playground/validator.v8" | ||||
| ) | ||||
|  | ||||
| @ -45,36 +44,11 @@ type redis struct { | ||||
| 	DB       string | ||||
| } | ||||
|  | ||||
| // RedisConfig Redis服务器配置 | ||||
| var RedisConfig = &redis{ | ||||
| 	Server:   "", | ||||
| 	Password: "", | ||||
| 	DB:       "0", | ||||
| } | ||||
|  | ||||
| // DatabaseConfig 数据库配置 | ||||
| var DatabaseConfig = &database{ | ||||
| 	Type: "UNSET", | ||||
| } | ||||
|  | ||||
| // SystemConfig 系统公用配置 | ||||
| var SystemConfig = &system{ | ||||
| 	Debug: false, | ||||
| } | ||||
|  | ||||
| // CaptchaConfig 验证码配置 | ||||
| var CaptchaConfig = &captcha{ | ||||
| 	Height:             60, | ||||
| 	Width:              240, | ||||
| 	Mode:               3, | ||||
| 	ComplexOfNoiseText: base64Captcha.CaptchaComplexLower, | ||||
| 	ComplexOfNoiseDot:  base64Captcha.CaptchaComplexLower, | ||||
| 	IsShowHollowLine:   false, | ||||
| 	IsShowNoiseDot:     false, | ||||
| 	IsShowNoiseText:    false, | ||||
| 	IsShowSlimeLine:    false, | ||||
| 	IsShowSineLine:     false, | ||||
| 	CaptchaLen:         6, | ||||
| // 缩略图 配置 | ||||
| type thumb struct { | ||||
| 	MaxWidth   uint | ||||
| 	MaxHeight  uint | ||||
| 	FileSuffix string `validate:"min=1"` | ||||
| } | ||||
|  | ||||
| var cfg *ini.File | ||||
| @ -90,10 +64,11 @@ func Init(path string) { | ||||
| 	} | ||||
|  | ||||
| 	sections := map[string]interface{}{ | ||||
| 		"Database": DatabaseConfig, | ||||
| 		"System":   SystemConfig, | ||||
| 		"Captcha":  CaptchaConfig, | ||||
| 		"Redis":    RedisConfig, | ||||
| 		"Database":  DatabaseConfig, | ||||
| 		"System":    SystemConfig, | ||||
| 		"Captcha":   CaptchaConfig, | ||||
| 		"Redis":     RedisConfig, | ||||
| 		"Thumbnail": ThumbConfig, | ||||
| 	} | ||||
| 	for sectionName, sectionStruct := range sections { | ||||
| 		err = mapSection(sectionName, sectionStruct) | ||||
|  | ||||
							
								
								
									
										41
									
								
								pkg/conf/defaults.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										41
									
								
								pkg/conf/defaults.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,41 @@ | ||||
| package conf | ||||
|  | ||||
| import "github.com/mojocn/base64Captcha" | ||||
|  | ||||
| // RedisConfig Redis服务器配置 | ||||
| var RedisConfig = &redis{ | ||||
| 	Server:   "", | ||||
| 	Password: "", | ||||
| 	DB:       "0", | ||||
| } | ||||
|  | ||||
| // DatabaseConfig 数据库配置 | ||||
| var DatabaseConfig = &database{ | ||||
| 	Type: "UNSET", | ||||
| } | ||||
|  | ||||
| // SystemConfig 系统公用配置 | ||||
| var SystemConfig = &system{ | ||||
| 	Debug: false, | ||||
| } | ||||
|  | ||||
| // CaptchaConfig 验证码配置 | ||||
| var CaptchaConfig = &captcha{ | ||||
| 	Height:             60, | ||||
| 	Width:              240, | ||||
| 	Mode:               3, | ||||
| 	ComplexOfNoiseText: base64Captcha.CaptchaComplexLower, | ||||
| 	ComplexOfNoiseDot:  base64Captcha.CaptchaComplexLower, | ||||
| 	IsShowHollowLine:   false, | ||||
| 	IsShowNoiseDot:     false, | ||||
| 	IsShowNoiseText:    false, | ||||
| 	IsShowSlimeLine:    false, | ||||
| 	IsShowSineLine:     false, | ||||
| 	CaptchaLen:         6, | ||||
| } | ||||
|  | ||||
| var ThumbConfig = &thumb{ | ||||
| 	MaxWidth:   400, | ||||
| 	MaxHeight:  300, | ||||
| 	FileSuffix: "._thumb", | ||||
| } | ||||
| @ -4,6 +4,7 @@ import ( | ||||
| 	"context" | ||||
| 	"fmt" | ||||
| 	model "github.com/HFO4/cloudreve/models" | ||||
| 	"github.com/HFO4/cloudreve/pkg/conf" | ||||
| 	"github.com/HFO4/cloudreve/pkg/filesystem/response" | ||||
| 	"github.com/HFO4/cloudreve/pkg/thumb" | ||||
| 	"github.com/HFO4/cloudreve/pkg/util" | ||||
| @ -54,7 +55,7 @@ func (fs *FileSystem) GenerateThumbnail(ctx context.Context, file *model.File) { | ||||
|  | ||||
| 	image, err := thumb.NewThumbFromFile(source, file.Name) | ||||
| 	if err != nil { | ||||
| 		util.Log().Warning("生成缩略图时无法解析[%s]图像数据:%s", file.SourceName, err) | ||||
| 		util.Log().Warning("生成缩略图时无法解析 [%s] 图像数据:%s", file.SourceName, err) | ||||
| 		return | ||||
| 	} | ||||
|  | ||||
| @ -64,9 +65,9 @@ func (fs *FileSystem) GenerateThumbnail(ctx context.Context, file *model.File) { | ||||
| 	// 生成缩略图 | ||||
| 	image.GetThumb(fs.GenerateThumbnailSize(w, h)) | ||||
| 	// 保存到文件 | ||||
| 	err = image.Save(file.SourceName + "._thumb") | ||||
| 	err = image.Save(file.SourceName + conf.ThumbConfig.FileSuffix) | ||||
| 	if err != nil { | ||||
| 		util.Log().Warning("无法保存缩略图:%s", file.SourceName, err) | ||||
| 		util.Log().Warning("无法保存缩略图:%s", err) | ||||
| 		return | ||||
| 	} | ||||
|  | ||||
| @ -75,12 +76,12 @@ func (fs *FileSystem) GenerateThumbnail(ctx context.Context, file *model.File) { | ||||
|  | ||||
| 	// 失败时删除缩略图文件 | ||||
| 	if err != nil { | ||||
| 		_, _ = fs.Handler.Delete(newCtx, []string{file.SourceName + "._thumb"}) | ||||
| 		_, _ = fs.Handler.Delete(newCtx, []string{file.SourceName + conf.ThumbConfig.FileSuffix}) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // GenerateThumbnailSize 获取要生成的缩略图的尺寸 | ||||
| // TODO 从配置文件读取 | ||||
| // TODO 优先从数据库中获得 | ||||
| func (fs *FileSystem) GenerateThumbnailSize(w, h int) (uint, uint) { | ||||
| 	return 400, 300 | ||||
| 	return conf.ThumbConfig.MaxWidth, conf.ThumbConfig.MaxHeight | ||||
| } | ||||
|  | ||||
							
								
								
									
										156
									
								
								pkg/filesystem/image_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										156
									
								
								pkg/filesystem/image_test.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,156 @@ | ||||
| package filesystem | ||||
|  | ||||
| import ( | ||||
| 	"context" | ||||
| 	"errors" | ||||
| 	"fmt" | ||||
| 	"github.com/DATA-DOG/go-sqlmock" | ||||
| 	model "github.com/HFO4/cloudreve/models" | ||||
| 	"github.com/HFO4/cloudreve/pkg/conf" | ||||
| 	"github.com/HFO4/cloudreve/pkg/filesystem/response" | ||||
| 	"github.com/HFO4/cloudreve/pkg/util" | ||||
| 	"github.com/jinzhu/gorm" | ||||
| 	"github.com/stretchr/testify/assert" | ||||
| 	testMock "github.com/stretchr/testify/mock" | ||||
| 	"image" | ||||
| 	"image/jpeg" | ||||
| 	"os" | ||||
| 	"testing" | ||||
| ) | ||||
|  | ||||
| func CreateTestImage() *os.File { | ||||
| 	file, err := os.Create("TestFileSystem_GenerateThumbnail.jpeg") | ||||
| 	alpha := image.NewAlpha(image.Rect(0, 0, 500, 200)) | ||||
| 	jpeg.Encode(file, alpha, nil) | ||||
| 	if err != nil { | ||||
| 		fmt.Println(err) | ||||
| 	} | ||||
| 	_, _ = file.Seek(0, 0) | ||||
| 	return file | ||||
| } | ||||
|  | ||||
| func TestFileSystem_GetThumb(t *testing.T) { | ||||
| 	asserts := assert.New(t) | ||||
| 	fs := FileSystem{ | ||||
| 		User: &model.User{ | ||||
| 			Model: gorm.Model{ID: 1}, | ||||
| 		}, | ||||
| 	} | ||||
| 	ctx := context.Background() | ||||
|  | ||||
| 	// 正常 | ||||
| 	{ | ||||
| 		testHandler := new(FileHeaderMock) | ||||
| 		testHandler.On("Thumb", testMock.Anything, "123.jpg").Return(&response.ContentResponse{URL: "123"}, nil) | ||||
| 		fs.Handler = testHandler | ||||
| 		mock.ExpectQuery("SELECT(.+)"). | ||||
| 			WithArgs(10, 1). | ||||
| 			WillReturnRows( | ||||
| 				sqlmock.NewRows( | ||||
| 					[]string{"id", "pic_info", "source_name"}). | ||||
| 					AddRow(10, "10,10", "123.jpg"), | ||||
| 			) | ||||
|  | ||||
| 		res, err := fs.GetThumb(ctx, 10) | ||||
| 		asserts.NoError(mock.ExpectationsWereMet()) | ||||
| 		testHandler.AssertExpectations(t) | ||||
| 		asserts.NoError(err) | ||||
| 		asserts.Equal("123", res.URL) | ||||
| 	} | ||||
|  | ||||
| 	// 文件不存在 | ||||
| 	{ | ||||
|  | ||||
| 		mock.ExpectQuery("SELECT(.+)"). | ||||
| 			WithArgs(10, 1). | ||||
| 			WillReturnRows( | ||||
| 				sqlmock.NewRows( | ||||
| 					[]string{"id", "pic_info", "source_name"}), | ||||
| 			) | ||||
|  | ||||
| 		_, err := fs.GetThumb(ctx, 10) | ||||
| 		asserts.NoError(mock.ExpectationsWereMet()) | ||||
| 		asserts.Error(err) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func TestFileSystem_GenerateThumbnail(t *testing.T) { | ||||
| 	asserts := assert.New(t) | ||||
| 	fs := FileSystem{ | ||||
| 		User: &model.User{ | ||||
| 			Model: gorm.Model{ID: 1}, | ||||
| 		}, | ||||
| 	} | ||||
| 	ctx := context.Background() | ||||
|  | ||||
| 	// 成功 | ||||
| 	{ | ||||
| 		src := CreateTestImage() | ||||
| 		testHandler := new(FileHeaderMock) | ||||
| 		testHandler.On("Get", testMock.Anything, "TestFileSystem_GenerateThumbnail.jpeg").Return(src, nil) | ||||
| 		fs.Handler = testHandler | ||||
|  | ||||
| 		mock.ExpectBegin() | ||||
| 		mock.ExpectExec("UPDATE(.+)").WillReturnResult(sqlmock.NewResult(1, 1)) | ||||
| 		mock.ExpectCommit() | ||||
|  | ||||
| 		file := &model.File{ | ||||
| 			Name:       "123.jpg", | ||||
| 			SourceName: "TestFileSystem_GenerateThumbnail.jpeg", | ||||
| 		} | ||||
|  | ||||
| 		fs.GenerateThumbnail(ctx, file) | ||||
| 		asserts.NoError(mock.ExpectationsWereMet()) | ||||
| 		testHandler.AssertExpectations(t) | ||||
| 		asserts.True(util.Exists("TestFileSystem_GenerateThumbnail.jpeg" + conf.ThumbConfig.FileSuffix)) | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	// 更新信息失败后删除文件 | ||||
| 	{ | ||||
| 		src := CreateTestImage() | ||||
| 		testHandler := new(FileHeaderMock) | ||||
| 		testHandler.On("Get", testMock.Anything, "TestFileSystem_GenerateThumbnail.jpeg").Return(src, nil) | ||||
| 		testHandler.On("Delete", testMock.Anything, testMock.Anything).Return([]string{}, nil) | ||||
| 		fs.Handler = testHandler | ||||
|  | ||||
| 		mock.ExpectBegin() | ||||
| 		mock.ExpectExec("UPDATE(.+)").WillReturnError(errors.New("error")) | ||||
| 		mock.ExpectRollback() | ||||
|  | ||||
| 		file := &model.File{ | ||||
| 			Name:       "123.jpg", | ||||
| 			SourceName: "TestFileSystem_GenerateThumbnail.jpeg", | ||||
| 		} | ||||
|  | ||||
| 		fs.GenerateThumbnail(ctx, file) | ||||
| 		asserts.NoError(mock.ExpectationsWereMet()) | ||||
| 		testHandler.AssertExpectations(t) | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	// 不能生成缩略图 | ||||
| 	{ | ||||
| 		file := &model.File{ | ||||
| 			Name:       "123.123", | ||||
| 			SourceName: "TestFileSystem_GenerateThumbnail.jpeg", | ||||
| 		} | ||||
|  | ||||
| 		fs.GenerateThumbnail(ctx, file) | ||||
| 		asserts.NoError(mock.ExpectationsWereMet()) | ||||
| 	} | ||||
|  | ||||
| } | ||||
|  | ||||
| func TestFileSystem_GenerateThumbnailSize(t *testing.T) { | ||||
| 	asserts := assert.New(t) | ||||
| 	fs := FileSystem{ | ||||
| 		User: &model.User{ | ||||
| 			Model: gorm.Model{ID: 1}, | ||||
| 		}, | ||||
| 	} | ||||
| 	asserts.NotPanics(func() { | ||||
| 		_, _ = fs.GenerateThumbnailSize(0, 0) | ||||
| 	}) | ||||
|  | ||||
| } | ||||
| @ -2,6 +2,7 @@ package local | ||||
|  | ||||
| import ( | ||||
| 	"context" | ||||
| 	"github.com/HFO4/cloudreve/pkg/conf" | ||||
| 	"github.com/HFO4/cloudreve/pkg/filesystem/response" | ||||
| 	"github.com/HFO4/cloudreve/pkg/util" | ||||
| 	"io" | ||||
| @ -78,7 +79,7 @@ func (handler Handler) Delete(ctx context.Context, files []string) ([]string, er | ||||
| 		} | ||||
|  | ||||
| 		// 尝试删除文件的缩略图(如果有) | ||||
| 		_ = os.Remove(value + "._thumb") | ||||
| 		_ = os.Remove(value + conf.ThumbConfig.FileSuffix) | ||||
| 	} | ||||
|  | ||||
| 	return deleteFailed, retErr | ||||
| @ -86,7 +87,7 @@ func (handler Handler) Delete(ctx context.Context, files []string) ([]string, er | ||||
|  | ||||
| // Thumb 获取文件缩略图 | ||||
| func (handler Handler) Thumb(ctx context.Context, path string) (*response.ContentResponse, error) { | ||||
| 	file, err := handler.Get(ctx, path+"._thumb") | ||||
| 	file, err := handler.Get(ctx, path+conf.ThumbConfig.FileSuffix) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
|  | ||||
							
								
								
									
										101
									
								
								pkg/thumb/image_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										101
									
								
								pkg/thumb/image_test.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,101 @@ | ||||
| package thumb | ||||
|  | ||||
| import ( | ||||
| 	"fmt" | ||||
| 	"github.com/HFO4/cloudreve/pkg/util" | ||||
| 	"github.com/stretchr/testify/assert" | ||||
| 	"image" | ||||
| 	"image/jpeg" | ||||
| 	"os" | ||||
| 	"testing" | ||||
| ) | ||||
|  | ||||
| func CreateTestImage() *os.File { | ||||
| 	file, err := os.Create("TestNewThumbFromFile.jpeg") | ||||
| 	alpha := image.NewAlpha(image.Rect(0, 0, 500, 200)) | ||||
| 	jpeg.Encode(file, alpha, nil) | ||||
| 	if err != nil { | ||||
| 		fmt.Println(err) | ||||
| 	} | ||||
| 	_, _ = file.Seek(0, 0) | ||||
| 	return file | ||||
| } | ||||
|  | ||||
| func TestNewThumbFromFile(t *testing.T) { | ||||
| 	asserts := assert.New(t) | ||||
| 	file := CreateTestImage() | ||||
| 	defer file.Close() | ||||
|  | ||||
| 	// 无扩展名时 | ||||
| 	{ | ||||
| 		thumb, err := NewThumbFromFile(file, "123") | ||||
| 		asserts.Error(err) | ||||
| 		asserts.Nil(thumb) | ||||
| 	} | ||||
|  | ||||
| 	{ | ||||
| 		thumb, err := NewThumbFromFile(file, "123.jpg") | ||||
| 		asserts.NoError(err) | ||||
| 		asserts.NotNil(thumb) | ||||
| 	} | ||||
| 	{ | ||||
| 		thumb, err := NewThumbFromFile(file, "123.jpeg") | ||||
| 		asserts.Error(err) | ||||
| 		asserts.Nil(thumb) | ||||
| 	} | ||||
| 	{ | ||||
| 		thumb, err := NewThumbFromFile(file, "123.png") | ||||
| 		asserts.Error(err) | ||||
| 		asserts.Nil(thumb) | ||||
| 	} | ||||
| 	{ | ||||
| 		thumb, err := NewThumbFromFile(file, "123.gif") | ||||
| 		asserts.Error(err) | ||||
| 		asserts.Nil(thumb) | ||||
| 	} | ||||
| 	{ | ||||
| 		thumb, err := NewThumbFromFile(file, "123.3211") | ||||
| 		asserts.Error(err) | ||||
| 		asserts.Nil(thumb) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func TestThumb_GetSize(t *testing.T) { | ||||
| 	asserts := assert.New(t) | ||||
| 	file := CreateTestImage() | ||||
| 	defer file.Close() | ||||
| 	thumb, err := NewThumbFromFile(file, "123.jpg") | ||||
| 	asserts.NoError(err) | ||||
|  | ||||
| 	w, h := thumb.GetSize() | ||||
| 	asserts.Equal(500, w) | ||||
| 	asserts.Equal(200, h) | ||||
| } | ||||
|  | ||||
| func TestThumb_GetThumb(t *testing.T) { | ||||
| 	asserts := assert.New(t) | ||||
| 	file := CreateTestImage() | ||||
| 	defer file.Close() | ||||
| 	thumb, err := NewThumbFromFile(file, "123.jpg") | ||||
| 	asserts.NoError(err) | ||||
|  | ||||
| 	asserts.NotPanics(func() { | ||||
| 		thumb.GetThumb(10, 10) | ||||
| 	}) | ||||
| } | ||||
|  | ||||
| func TestThumb_Save(t *testing.T) { | ||||
| 	asserts := assert.New(t) | ||||
| 	file := CreateTestImage() | ||||
| 	defer file.Close() | ||||
| 	thumb, err := NewThumbFromFile(file, "123.jpg") | ||||
| 	asserts.NoError(err) | ||||
|  | ||||
| 	err = thumb.Save("/:noteexist/") | ||||
| 	asserts.Error(err) | ||||
|  | ||||
| 	err = thumb.Save("TestThumb_Save.png") | ||||
| 	asserts.NoError(err) | ||||
| 	asserts.True(util.Exists("TestThumb_Save.png")) | ||||
|  | ||||
| } | ||||
| @ -20,7 +20,7 @@ func StartLoginAuthn(c *gin.Context) { | ||||
| 		return | ||||
| 	} | ||||
|  | ||||
| 	options, sessionData, err := authn.Authn.BeginLogin(expectedUser) | ||||
| 	options, sessionData, err := authn.AuthnInstance.BeginLogin(expectedUser) | ||||
| 	if err != nil { | ||||
| 		c.JSON(200, ErrorResponse(err)) | ||||
| 		return | ||||
| @ -52,7 +52,7 @@ func FinishLoginAuthn(c *gin.Context) { | ||||
| 	var sessionData webauthn.SessionData | ||||
| 	err = json.Unmarshal(sessionDataJSON, &sessionData) | ||||
|  | ||||
| 	_, err = authn.Authn.FinishLogin(expectedUser, sessionData, c.Request) | ||||
| 	_, err = authn.AuthnInstance.FinishLogin(expectedUser, sessionData, c.Request) | ||||
|  | ||||
| 	if err != nil { | ||||
| 		c.JSON(200, serializer.Err(401, "用户邮箱或密码错误", err)) | ||||
| @ -68,7 +68,7 @@ func FinishLoginAuthn(c *gin.Context) { | ||||
| // StartRegAuthn 开始注册WebAuthn信息 | ||||
| func StartRegAuthn(c *gin.Context) { | ||||
| 	currUser := CurrentUser(c) | ||||
| 	options, sessionData, err := authn.Authn.BeginRegistration(currUser) | ||||
| 	options, sessionData, err := authn.AuthnInstance.BeginRegistration(currUser) | ||||
| 	if err != nil { | ||||
| 		c.JSON(200, ErrorResponse(err)) | ||||
| 		return | ||||
| @ -94,7 +94,7 @@ func FinishRegAuthn(c *gin.Context) { | ||||
| 	var sessionData webauthn.SessionData | ||||
| 	err := json.Unmarshal(sessionDataJSON, &sessionData) | ||||
|  | ||||
| 	credential, err := authn.Authn.FinishRegistration(currUser, sessionData, c.Request) | ||||
| 	credential, err := authn.AuthnInstance.FinishRegistration(currUser, sessionData, c.Request) | ||||
|  | ||||
| 	currUser.RegisterAuthn(credential) | ||||
| 	if err != nil { | ||||
|  | ||||
		Reference in New Issue
	
	Block a user
	 HFO4
					HFO4