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 | Debug = true | ||||||
| SessionSecret = 23333 | SessionSecret = 23333 | ||||||
|  |  | ||||||
|  | [Thumbnail] | ||||||
|  | MaxWidth = 400 | ||||||
|  | MaxHeight = 300 | ||||||
|  | FileSuffix = ._thumb | ||||||
|  |  | ||||||
| [Database] | [Database] | ||||||
| Type = mysql | Type = mysql | ||||||
| User = root | User = root | ||||||
|  | |||||||
| @ -2,12 +2,9 @@ package model | |||||||
|  |  | ||||||
| import ( | import ( | ||||||
| 	"crypto/sha1" | 	"crypto/sha1" | ||||||
| 	"encoding/binary" |  | ||||||
| 	"encoding/hex" | 	"encoding/hex" | ||||||
| 	"encoding/json" | 	"encoding/json" | ||||||
| 	"fmt" |  | ||||||
| 	"github.com/HFO4/cloudreve/pkg/util" | 	"github.com/HFO4/cloudreve/pkg/util" | ||||||
| 	"github.com/duo-labs/webauthn/webauthn" |  | ||||||
| 	"github.com/jinzhu/gorm" | 	"github.com/jinzhu/gorm" | ||||||
| 	"github.com/pkg/errors" | 	"github.com/pkg/errors" | ||||||
| 	"strings" | 	"strings" | ||||||
| @ -59,41 +56,6 @@ type UserOption struct { | |||||||
| 	PreferredTheme  string `json:"preferred_theme"` | 	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 获取用户的根目录 | // Root 获取用户的根目录 | ||||||
| func (user *User) Root() (*Folder, error) { | func (user *User) Root() (*Folder, error) { | ||||||
| 	var folder Folder | 	var folder Folder | ||||||
| @ -157,18 +119,17 @@ func (user *User) GetPolicyID() uint { | |||||||
| 			return user.Group.PolicyList[0] | 			return user.Group.PolicyList[0] | ||||||
| 		} | 		} | ||||||
| 		return 1 | 		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获取用户 | // 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" | 	"github.com/duo-labs/webauthn/webauthn" | ||||||
| ) | ) | ||||||
|  |  | ||||||
| var Authn *webauthn.WebAuthn | var AuthnInstance *webauthn.WebAuthn | ||||||
|  |  | ||||||
| func Init() { | func Init() { | ||||||
| 	var err error | 	var err error | ||||||
| 	Authn, err = webauthn.New(&webauthn.Config{ | 	AuthnInstance, err = webauthn.New(&webauthn.Config{ | ||||||
| 		RPDisplayName: "Duo Labs",                 // Display Name for your site | 		RPDisplayName: "Duo Labs",                 // Display Name for your site | ||||||
| 		RPID:          "localhost",                // Generally the FQDN for your site | 		RPID:          "localhost",                // Generally the FQDN for your site | ||||||
| 		RPOrigin:      "http://localhost:3000",    // The origin URL for WebAuthn requests | 		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 ( | import ( | ||||||
| 	"github.com/HFO4/cloudreve/pkg/util" | 	"github.com/HFO4/cloudreve/pkg/util" | ||||||
| 	"github.com/go-ini/ini" | 	"github.com/go-ini/ini" | ||||||
| 	"github.com/mojocn/base64Captcha" |  | ||||||
| 	"gopkg.in/go-playground/validator.v8" | 	"gopkg.in/go-playground/validator.v8" | ||||||
| ) | ) | ||||||
|  |  | ||||||
| @ -45,36 +44,11 @@ type redis struct { | |||||||
| 	DB       string | 	DB       string | ||||||
| } | } | ||||||
|  |  | ||||||
| // RedisConfig Redis服务器配置 | // 缩略图 配置 | ||||||
| var RedisConfig = &redis{ | type thumb struct { | ||||||
| 	Server:   "", | 	MaxWidth   uint | ||||||
| 	Password: "", | 	MaxHeight  uint | ||||||
| 	DB:       "0", | 	FileSuffix string `validate:"min=1"` | ||||||
| } |  | ||||||
|  |  | ||||||
| // 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 cfg *ini.File | var cfg *ini.File | ||||||
| @ -90,10 +64,11 @@ func Init(path string) { | |||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	sections := map[string]interface{}{ | 	sections := map[string]interface{}{ | ||||||
| 		"Database": DatabaseConfig, | 		"Database":  DatabaseConfig, | ||||||
| 		"System":   SystemConfig, | 		"System":    SystemConfig, | ||||||
| 		"Captcha":  CaptchaConfig, | 		"Captcha":   CaptchaConfig, | ||||||
| 		"Redis":    RedisConfig, | 		"Redis":     RedisConfig, | ||||||
|  | 		"Thumbnail": ThumbConfig, | ||||||
| 	} | 	} | ||||||
| 	for sectionName, sectionStruct := range sections { | 	for sectionName, sectionStruct := range sections { | ||||||
| 		err = mapSection(sectionName, sectionStruct) | 		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" | 	"context" | ||||||
| 	"fmt" | 	"fmt" | ||||||
| 	model "github.com/HFO4/cloudreve/models" | 	model "github.com/HFO4/cloudreve/models" | ||||||
|  | 	"github.com/HFO4/cloudreve/pkg/conf" | ||||||
| 	"github.com/HFO4/cloudreve/pkg/filesystem/response" | 	"github.com/HFO4/cloudreve/pkg/filesystem/response" | ||||||
| 	"github.com/HFO4/cloudreve/pkg/thumb" | 	"github.com/HFO4/cloudreve/pkg/thumb" | ||||||
| 	"github.com/HFO4/cloudreve/pkg/util" | 	"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) | 	image, err := thumb.NewThumbFromFile(source, file.Name) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		util.Log().Warning("生成缩略图时无法解析[%s]图像数据:%s", file.SourceName, err) | 		util.Log().Warning("生成缩略图时无法解析 [%s] 图像数据:%s", file.SourceName, err) | ||||||
| 		return | 		return | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| @ -64,9 +65,9 @@ func (fs *FileSystem) GenerateThumbnail(ctx context.Context, file *model.File) { | |||||||
| 	// 生成缩略图 | 	// 生成缩略图 | ||||||
| 	image.GetThumb(fs.GenerateThumbnailSize(w, h)) | 	image.GetThumb(fs.GenerateThumbnailSize(w, h)) | ||||||
| 	// 保存到文件 | 	// 保存到文件 | ||||||
| 	err = image.Save(file.SourceName + "._thumb") | 	err = image.Save(file.SourceName + conf.ThumbConfig.FileSuffix) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		util.Log().Warning("无法保存缩略图:%s", file.SourceName, err) | 		util.Log().Warning("无法保存缩略图:%s", err) | ||||||
| 		return | 		return | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| @ -75,12 +76,12 @@ func (fs *FileSystem) GenerateThumbnail(ctx context.Context, file *model.File) { | |||||||
|  |  | ||||||
| 	// 失败时删除缩略图文件 | 	// 失败时删除缩略图文件 | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		_, _ = fs.Handler.Delete(newCtx, []string{file.SourceName + "._thumb"}) | 		_, _ = fs.Handler.Delete(newCtx, []string{file.SourceName + conf.ThumbConfig.FileSuffix}) | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
| // GenerateThumbnailSize 获取要生成的缩略图的尺寸 | // GenerateThumbnailSize 获取要生成的缩略图的尺寸 | ||||||
| // TODO 从配置文件读取 | // TODO 优先从数据库中获得 | ||||||
| func (fs *FileSystem) GenerateThumbnailSize(w, h int) (uint, uint) { | 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 ( | import ( | ||||||
| 	"context" | 	"context" | ||||||
|  | 	"github.com/HFO4/cloudreve/pkg/conf" | ||||||
| 	"github.com/HFO4/cloudreve/pkg/filesystem/response" | 	"github.com/HFO4/cloudreve/pkg/filesystem/response" | ||||||
| 	"github.com/HFO4/cloudreve/pkg/util" | 	"github.com/HFO4/cloudreve/pkg/util" | ||||||
| 	"io" | 	"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 | 	return deleteFailed, retErr | ||||||
| @ -86,7 +87,7 @@ func (handler Handler) Delete(ctx context.Context, files []string) ([]string, er | |||||||
|  |  | ||||||
| // Thumb 获取文件缩略图 | // Thumb 获取文件缩略图 | ||||||
| func (handler Handler) Thumb(ctx context.Context, path string) (*response.ContentResponse, error) { | 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 { | 	if err != nil { | ||||||
| 		return nil, err | 		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 | 		return | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	options, sessionData, err := authn.Authn.BeginLogin(expectedUser) | 	options, sessionData, err := authn.AuthnInstance.BeginLogin(expectedUser) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		c.JSON(200, ErrorResponse(err)) | 		c.JSON(200, ErrorResponse(err)) | ||||||
| 		return | 		return | ||||||
| @ -52,7 +52,7 @@ func FinishLoginAuthn(c *gin.Context) { | |||||||
| 	var sessionData webauthn.SessionData | 	var sessionData webauthn.SessionData | ||||||
| 	err = json.Unmarshal(sessionDataJSON, &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 { | 	if err != nil { | ||||||
| 		c.JSON(200, serializer.Err(401, "用户邮箱或密码错误", err)) | 		c.JSON(200, serializer.Err(401, "用户邮箱或密码错误", err)) | ||||||
| @ -68,7 +68,7 @@ func FinishLoginAuthn(c *gin.Context) { | |||||||
| // StartRegAuthn 开始注册WebAuthn信息 | // StartRegAuthn 开始注册WebAuthn信息 | ||||||
| func StartRegAuthn(c *gin.Context) { | func StartRegAuthn(c *gin.Context) { | ||||||
| 	currUser := CurrentUser(c) | 	currUser := CurrentUser(c) | ||||||
| 	options, sessionData, err := authn.Authn.BeginRegistration(currUser) | 	options, sessionData, err := authn.AuthnInstance.BeginRegistration(currUser) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		c.JSON(200, ErrorResponse(err)) | 		c.JSON(200, ErrorResponse(err)) | ||||||
| 		return | 		return | ||||||
| @ -94,7 +94,7 @@ func FinishRegAuthn(c *gin.Context) { | |||||||
| 	var sessionData webauthn.SessionData | 	var sessionData webauthn.SessionData | ||||||
| 	err := json.Unmarshal(sessionDataJSON, &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) | 	currUser.RegisterAuthn(credential) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
|  | |||||||
		Reference in New Issue
	
	Block a user
	 HFO4
					HFO4