Modify: clean modals

pull/247/head
HFO4 5 years ago
parent 4d70f9fa3e
commit 4ba24e0cf1

@ -1 +1 @@
Subproject commit d46074216363ac62a66a02284f2a791b79a392cd
Subproject commit 186f0aac0964c95ed4826894ddf888e32e00d46b

@ -1,23 +1,10 @@
package bootstrap
import (
"bytes"
"crypto/aes"
"crypto/cipher"
"encoding/gob"
"fmt"
"github.com/HFO4/cloudreve/bootstrap/constant"
"github.com/HFO4/cloudreve/pkg/conf"
"github.com/HFO4/cloudreve/pkg/util"
"github.com/gin-gonic/gin"
"io/ioutil"
"os"
"strconv"
)
var matrix []byte
var APPID string
// InitApplication 初始化应用常量
func InitApplication() {
fmt.Print(`
@ -31,65 +18,4 @@ V` + conf.BackendVersion + ` Commit #` + conf.LastCommit + ` Pro=` + conf.IsPr
================================================
`)
data, err := ioutil.ReadFile(util.RelativePath(string([]byte{107, 101, 121, 46, 98, 105, 110})))
if err != nil {
util.Log().Panic("%s", err)
}
table := deSign(data)
constant.HashIDTable = table["table"].([]int)
APPID = table["id"].(string)
matrix = table["pic"].([]byte)
}
// InitCustomRoute 初始化自定义路由
func InitCustomRoute(group *gin.RouterGroup) {
group.GET(string([]byte{98, 103}), func(c *gin.Context) {
c.Header("content-type", "image/png")
c.Writer.Write(matrix)
})
group.GET("id", func(c *gin.Context) {
c.String(200, APPID)
})
}
func deSign(data []byte) map[string]interface{} {
res := decode(data, seed())
dec := gob.NewDecoder(bytes.NewReader(res))
obj := map[string]interface{}{}
err := dec.Decode(&obj)
if err != nil {
fmt.Println(err)
os.Exit(-1)
}
return obj
}
func seed() []byte {
res := []int{10}
s := "2020"
m := 1 << 20
a := 9
b := 7
for i := 1; i < 23; i++ {
res = append(res, (a*res[i-1]+b)%m)
s += strconv.Itoa(res[i])
}
return []byte(s)
}
func decode(cryted []byte, key []byte) []byte {
block, _ := aes.NewCipher(key[:32])
blockSize := block.BlockSize()
blockMode := cipher.NewCBCDecrypter(block, key[:blockSize])
orig := make([]byte, len(cryted))
blockMode.CryptBlocks(orig, cryted)
orig = pKCS7UnPadding(orig)
return orig
}
func pKCS7UnPadding(origData []byte) []byte {
length := len(origData)
unpadding := int(origData[length-1])
return origData[:(length - unpadding)]
}

@ -1,3 +0,0 @@
package constant
var HashIDTable = []int{}

@ -1,7 +1,6 @@
package middleware
import (
"github.com/HFO4/cloudreve/bootstrap/constant"
"github.com/HFO4/cloudreve/pkg/cache"
"github.com/HFO4/cloudreve/pkg/hashid"
"github.com/gin-gonic/gin"
@ -15,7 +14,6 @@ func TestHashID(t *testing.T) {
asserts := assert.New(t)
rec := httptest.NewRecorder()
TestFunc := HashID(hashid.FolderID)
constant.HashIDTable = []int{0, 1, 2, 3, 4, 5, 6}
// 未给定ID对象跳过
{

@ -15,7 +15,6 @@ type Folder struct {
Name string `gorm:"unique_index:idx_only_one_name"`
ParentID *uint `gorm:"index:parent_id;unique_index:idx_only_one_name"`
OwnerID uint `gorm:"index:owner_id"`
PolicyID uint // Webdav下挂载的存储策略ID
// 数据库忽略字段
Position string `gorm:"-"`
@ -30,13 +29,6 @@ func (folder *Folder) Create() (uint, error) {
return folder.ID, nil
}
// GetMountedFolders 列出已挂载存储策略的目录
func GetMountedFolders(uid uint) []Folder {
var folders []Folder
DB.Where("owner_id = ? and policy_id <> ?", uid, 0).Find(&folders)
return folders
}
// GetChild 返回folder下名为name的子目录不存在则返回错误
func (folder *Folder) GetChild(name string) (*Folder, error) {
var resFolder Folder
@ -272,11 +264,6 @@ func (folder *Folder) Rename(new string) error {
return nil
}
// Mount 目录挂载
func (folder *Folder) Mount(new uint) error {
return DB.Model(&folder).Update("policy_id", new).Error
}
/*
FileInfo.FileInfo
TODO

@ -29,7 +29,6 @@ type GroupOption struct {
DecompressSize uint64 `json:"decompress_size,omitempty"`
OneTimeDownload bool `json:"one_time_download,omitempty"`
ShareDownload bool `json:"share_download,omitempty"`
ShareFree bool `json:"share_free,omitempty"`
Aria2 bool `json:"aria2,omitempty"` // 离线下载
Aria2Options map[string]interface{} `json:"aria2_options,omitempty"` // 离线下载用户组配置
}

@ -23,7 +23,7 @@ func TestGetGroupByID(t *testing.T) {
ID: 1,
},
Name: "管理员",
Policies: "[1]",
PolicyID: 1,
PolicyList: []uint{1},
}, group)
@ -42,7 +42,7 @@ func TestGroup_AfterFind(t *testing.T) {
ID: 1,
},
Name: "管理员",
Policies: "[1]",
PolicyID: 1,
}
err := testCase.AfterFind()
asserts.NoError(err)

@ -35,8 +35,8 @@ func migration() {
if conf.DatabaseConfig.Type == "mysql" {
DB = DB.Set("gorm:table_options", "ENGINE=InnoDB")
}
DB.AutoMigrate(&User{}, &Setting{}, &Group{}, &Policy{}, &Folder{}, &File{}, &StoragePack{}, &Share{},
&Task{}, &Download{}, &Tag{}, &Webdav{}, &Order{}, &Redeem{})
DB.AutoMigrate(&User{}, &Setting{}, &Group{}, &Policy{}, &Folder{}, &File{}, &Share{},
&Task{}, &Download{}, &Tag{}, &Webdav{})
// 创建初始存储策略
addDefaultPolicy()
@ -90,9 +90,6 @@ func addDefaultSettings() {
{Name: "replyTo", Value: `abslant@126.com`, Type: "mail"},
{Name: "smtpUser", Value: `no-reply@acg.blue`, Type: "mail"},
{Name: "smtpPass", Value: ``, Type: "mail"},
{Name: "over_used_template", Value: `<meta name="viewport"content="width=device-width"><meta http-equiv="Content-Type"content="text/html; charset=UTF-8"><title></title><style type="text/css">img{max-width:100%}body{-webkit-font-smoothing:antialiased;-webkit-text-size-adjust:none;width:100%!important;height:100%;line-height:1.6em}body{background-color:#f6f6f6}@media only screen and(max-width:640px){body{padding:0!important}h1{font-weight:800!important;margin:20px 0 5px!important}h2{font-weight:800!important;margin:20px 0 5px!important}h3{font-weight:800!important;margin:20px 0 5px!important}h4{font-weight:800!important;margin:20px 0 5px!important}h1{font-size:22px!important}h2{font-size:18px!important}h3{font-size:16px!important}.container{padding:0!important;width:100%!important}.content{padding:0!important}.content-wrap{padding:10px!important}.invoice{width:100%!important}}</style><table class="body-wrap"style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; width: 100%; background-color: #f6f6f6; margin: 0;"bgcolor="#f6f6f6"><tbody><tr style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;"><td style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; vertical-align: top; margin: 0;"valign="top"></td><td class="container"width="600"style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; vertical-align: top; display: block !important; max-width: 600px !important; clear: both !important; margin: 0 auto;"valign="top"><div class="content"style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; max-width: 600px; display: block; margin: 0 auto; padding: 20px;"><table class="main"width="100%"cellpadding="0"cellspacing="0"style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; border-radius: 3px; background-color: #fff; margin: 0; border: 1px
solid #e9e9e9;"bgcolor="#fff"><tbody><tr style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;"><td class="alert alert-warning"style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 16px; vertical-align: top; color: #fff; font-weight: 500; text-align: center; border-radius: 3px 3px 0 0; background-color: #FF9F00; margin: 0; padding: 20px;"align="center"bgcolor="#FF9F00"valign="top">容量超额警告</td></tr><tr style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;"><td class="content-wrap"style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; vertical-align: top; margin: 0; padding: 20px;"valign="top"><table width="100%"cellpadding="0"cellspacing="0"style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;"><tbody><tr style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;"><td class="content-block"style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; vertical-align: top; margin: 0; padding: 0 0 20px;"valign="top">亲爱的<strong style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;">{userName}</strong></td></tr><tr style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;"><td class="content-block"style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; vertical-align: top; margin: 0; padding: 0 0 20px;"valign="top">由于{notifyReason},您在{siteTitle}的账户的容量使用超出配额,您将无法继续上传新文件,请尽快清理文件,否则我们将会禁用您的账户。</td></tr><tr style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;"><td class="content-block"style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; vertical-align: top; margin: 0; padding: 0 0 20px;"valign="top"><a href="{siteUrl}Login"class="btn-primary"style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; color: #FFF; text-decoration: none; line-height: 2em; font-weight: bold; text-align: center; cursor: pointer; display: inline-block; border-radius: 5px; text-transform: capitalize; background-color: #348eda; margin: 0; border-color: #348eda; border-style: solid; border-width: 10px 20px;">登录{siteTitle}</a></td></tr><tr style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;"><td class="content-block"style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; vertical-align: top; margin: 0; padding: 0 0 20px;"valign="top">感谢您选择{siteTitle}。</td></tr></tbody></table></td></tr></tbody></table><div class="footer"style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; width: 100%; clear: both; color: #999; margin: 0; padding: 20px;"><table width="100%"style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;"><tbody><tr style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;"><td class="aligncenter content-block"style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 12px; vertical-align: top; color: #999; text-align: center; margin: 0; padding: 0 0 20px;"align="center"valign="top">此邮件由系统自动发送,请不要直接回复。</td></tr></tbody></table></div></div></td><td style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; vertical-align: top; margin: 0;"valign="top"></td></tr></tbody></table>`, Type: "mail_template"},
{Name: "ban_time", Value: `604800`, Type: "storage_policy"},
{Name: "maxEditSize", Value: `4194304`, Type: "file_edit"},
{Name: "archive_timeout", Value: `60`, Type: "timeout"},
{Name: "download_timeout", Value: `60`, Type: "timeout"},
@ -108,10 +105,6 @@ solid #e9e9e9;"bgcolor="#fff"><tbody><tr style="font-family: 'Helvetica Neue',He
{Name: "onedrive_chunk_retries", Value: `1`, Type: "retry"},
{Name: "reset_after_upload_failed", Value: `0`, Type: "upload"},
{Name: "login_captcha", Value: `0`, Type: "login"},
{Name: "qq_login", Value: `0`, Type: "login"},
{Name: "qq_direct_login", Value: `0`, Type: "login"},
{Name: "qq_login_id", Value: ``, Type: "login"},
{Name: "qq_login_key", Value: ``, Type: "login"},
{Name: "reg_captcha", Value: `0`, Type: "login"},
{Name: "email_active", Value: `0`, Type: "register"},
{Name: "mail_activation_template", Value: `<!DOCTYPE html PUBLIC"-//W3C//DTD XHTML 1.0 Transitional//EN""http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"><html xmlns="http://www.w3.org/1999/xhtml"style="font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; box-sizing: border-box;
@ -129,17 +122,8 @@ box-sizing: border-box; font-size: 14px; margin: 0;"><td style="font-family: 'He
solid #e9e9e9;"bgcolor="#fff"><tr style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size:
14px; margin: 0;"><td class="alert alert-warning"style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 16px; vertical-align: top; color: #fff; font-weight: 500; text-align: center; border-radius: 3px 3px 0 0; background-color: #2196F3; margin: 0; padding: 20px;"align="center"bgcolor="#FF9F00"valign="top">重设{siteTitle}密码</td></tr><tr style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;"><td class="content-wrap"style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; vertical-align: top; margin: 0; padding: 20px;"valign="top"><table width="100%"cellpadding="0"cellspacing="0"style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;"><tr style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;"><td class="content-block"style="font-family: 'Helvetica
Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; vertical-align: top; margin: 0; padding: 0 0 20px;"valign="top">亲爱的<strong style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;">{userName}</strong></td></tr><tr style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;"><td class="content-block"style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; vertical-align: top; margin: 0; padding: 0 0 20px;"valign="top">请点击下方按钮完成密码重设。如果非你本人操作,请忽略此邮件。</td></tr><tr style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;"><td class="content-block"style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; vertical-align: top; margin: 0; padding: 0 0 20px;"valign="top"><a href="{resetUrl}"class="btn-primary"style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; color: #FFF; text-decoration: none; line-height: 2em; font-weight: bold; text-align: center; cursor: pointer; display: inline-block; border-radius: 5px; text-transform: capitalize; background-color: #2196F3; margin: 0; border-color: #2196F3; border-style: solid; border-width: 10px 20px;">重设密码</a></td></tr><tr style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;"><td class="content-block"style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; vertical-align: top; margin: 0; padding: 0 0 20px;"valign="top">感谢您选择{siteTitle}。</td></tr></table></td></tr></table><div class="footer"style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; width: 100%; clear: both; color: #999; margin: 0; padding: 20px;"><table width="100%"style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;"><tr style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;"><td class="aligncenter content-block"style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 12px; vertical-align: top; color: #999; text-align: center; margin: 0; padding: 0 0 20px;"align="center"valign="top">此邮件由系统自动发送,请不要直接回复。</td></tr></table></div></div></td><td style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; vertical-align: top; margin: 0;"valign="top"></td></tr></table></body></html>`, Type: "mail_template"},
{Name: "pack_data", Value: `[]`, Type: "pack"},
{Name: "database_version", Value: `3.0.0-alpha1`, Type: "version"},
{Name: "alipay_enabled", Value: `0`, Type: "payment"},
{Name: "payjs_enabled", Value: `0`, Type: "payment"},
{Name: "payjs_id", Value: ``, Type: "payment"},
{Name: "payjs_secret", Value: ``, Type: "payment"},
{Name: "appid", Value: ``, Type: "payment"},
{Name: "appkey", Value: ``, Type: "payment"},
{Name: "shopid", Value: ``, Type: "payment"},
{Name: "hot_share_num", Value: `10`, Type: "share"},
{Name: "group_sell_data", Value: `[]`, Type: "group_sell"},
{Name: "gravatar_server", Value: `https://www.gravatar.com/`, Type: "avatar"},
{Name: "defaultTheme", Value: `#3f51b5`, Type: "basic"},
{Name: "themes", Value: `{"#3f51b5":{"palette":{"primary":{"main":"#3f51b5"},"secondary":{"main":"#f50057"}}},"#2196f3":{"palette":{"primary":{"main":"#2196f3"},"secondary":{"main":"#FFC107"}}},"#673AB7":{"palette":{"primary":{"main":"#673AB7"},"secondary":{"main":"#2196F3"}}},"#E91E63":{"palette":{"primary":{"main":"#E91E63"},"secondary":{"main":"#42A5F5","contrastText":"#fff"}}},"#FF5722":{"palette":{"primary":{"main":"#FF5722"},"secondary":{"main":"#3F51B5"}}},"#FFC107":{"palette":{"primary":{"main":"#FFC107"},"secondary":{"main":"#26C6DA"}}},"#8BC34A":{"palette":{"primary":{"main":"#8BC34A","contrastText":"#fff"},"secondary":{"main":"#FF8A65","contrastText":"#fff"}}},"#009688":{"palette":{"primary":{"main":"#009688"},"secondary":{"main":"#4DD0E1","contrastText":"#fff"}}},"#607D8B":{"palette":{"primary":{"main":"#607D8B"},"secondary":{"main":"#F06292"}}},"#795548":{"palette":{"primary":{"main":"#795548"},"secondary":{"main":"#4CAF50","contrastText":"#fff"}}}}`, Type: "basic"},
@ -157,9 +141,6 @@ Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; verti
{Name: "avatar_size_l", Value: "200", Type: "avatar"},
{Name: "avatar_size_m", Value: "130", Type: "avatar"},
{Name: "avatar_size_s", Value: "50", Type: "avatar"},
{Name: "score_enabled", Value: "1", Type: "score"},
{Name: "share_score_rate", Value: "80", Type: "score"},
{Name: "score_price", Value: "1", Type: "score"},
{Name: "home_view_method", Value: "icon", Type: "view"},
{Name: "share_view_method", Value: "list", Type: "view"},
{Name: "cron_garbage_collect", Value: "@hourly", Type: "cron"},
@ -206,7 +187,6 @@ func addDefaultGroups() {
ArchiveDownload: true,
ArchiveTask: true,
ShareDownload: true,
ShareFree: true,
Aria2: true,
},
}

@ -1,59 +0,0 @@
package model
import (
"github.com/HFO4/cloudreve/pkg/util"
"github.com/jinzhu/gorm"
)
const (
// PackOrderType 容量包订单
PackOrderType = iota
// GroupOrderType 用户组订单
GroupOrderType
// ScoreOrderType 积分充值订单
ScoreOrderType
)
const (
// OrderUnpaid 未支付
OrderUnpaid = iota
// OrderPaid 已支付
OrderPaid
// OrderCanceled 已取消
OrderCanceled
)
// Order 交易订单
type Order struct {
gorm.Model
UserID uint // 创建者ID
OrderNo string `gorm:"index:order_number"` // 商户自定义订单编号
Type int // 订单类型
Method string // 支付类型
ProductID int64 // 商品ID
Num int // 商品数量
Name string // 订单标题
Price int // 商品单价
Status int // 订单状态
}
// Create 创建订单记录
func (order *Order) Create() (uint, error) {
if err := DB.Create(order).Error; err != nil {
util.Log().Warning("无法插入离线下载记录, %s", err)
return 0, err
}
return order.ID, nil
}
// UpdateStatus 更新订单状态
func (order *Order) UpdateStatus(status int) {
DB.Model(order).Update("status", status)
}
// GetOrderByNo 根据商户订单号查询订单
func GetOrderByNo(id string) (*Order, error) {
var order Order
err := DB.Where("order_no = ?", id).First(&order).Error
return &order, err
}

@ -1,27 +0,0 @@
package model
import "github.com/jinzhu/gorm"
// Redeem 兑换码
type Redeem struct {
gorm.Model
Type int // 订单类型
ProductID int64 // 商品ID
Num int // 商品数量
Code string `gorm:"size:64,index:redeem_code"` // 兑换码
Used bool // 是否已被使用
}
// GetAvailableRedeem 根据code查找可用兑换码
func GetAvailableRedeem(code string) (*Redeem, error) {
redeem := &Redeem{}
result := DB.Where("code = ? and used = ?", code, false).First(redeem)
return redeem, result.Error
}
// Use 设定为已使用状态
func (redeem *Redeem) Use() {
DB.Model(redeem).Updates(map[string]interface{}{
"used": true,
})
}

@ -8,7 +8,6 @@ import (
"github.com/HFO4/cloudreve/pkg/util"
"github.com/gin-gonic/gin"
"github.com/jinzhu/gorm"
"math"
"strings"
"time"
)
@ -24,7 +23,6 @@ type Share struct {
Downloads int // 下载数
RemainDownloads int // 剩余下载配额,负值标识无限制
Expires *time.Time // 过期时间,空值表示无过期时间
Score int // 每人次下载扣除积分
PreviewEnabled bool // 是否允许直接预览
SourceName string `gorm:"index:source"` // 用于搜索的字段
@ -136,12 +134,6 @@ func (share *Share) CanBeDownloadBy(user *User) error {
}
return errors.New("您当前的用户组无权下载")
}
// 需要积分但未登录
if share.Score > 0 && user.IsAnonymous() {
return errors.New("未登录用户无法下载")
}
return nil
}
@ -156,12 +148,9 @@ func (share *Share) WasDownloadedBy(user *User, c *gin.Context) (exist bool) {
return exist
}
// DownloadBy 增加下载次数、检查积分等,匿名用户不会缓存
// DownloadBy 增加下载次数,匿名用户不会缓存
func (share *Share) DownloadBy(user *User, c *gin.Context) error {
if !share.WasDownloadedBy(user, c) {
if err := share.Purchase(user); err != nil {
return err
}
share.Downloaded()
if !user.IsAnonymous() {
cache.Set(fmt.Sprintf("share_%d_%d", share.ID, user.ID), true,
@ -173,25 +162,6 @@ func (share *Share) DownloadBy(user *User, c *gin.Context) error {
return nil
}
// Purchase 使用积分购买分享
func (share *Share) Purchase(user *User) error {
// 不需要付积分
if share.Score == 0 || user.Group.OptionsSerialized.ShareFree || user.ID == share.UserID {
return nil
}
ok := user.PayScore(share.Score)
if !ok {
return errors.New("积分不足")
}
scoreRate := GetIntSetting("share_score_rate", 100)
gainedScore := int(math.Ceil(float64(share.Score*scoreRate) / 100))
share.Creator().AddScore(gainedScore)
return nil
}
// Viewed 增加访问次数
func (share *Share) Viewed() {
share.Views++

@ -1,91 +0,0 @@
package model
import (
"github.com/HFO4/cloudreve/pkg/cache"
"github.com/HFO4/cloudreve/pkg/util"
"github.com/jinzhu/gorm"
"strconv"
"time"
)
// StoragePack 容量包模型
type StoragePack struct {
// 表字段
gorm.Model
Name string
UserID uint
ActiveTime *time.Time
ExpiredTime *time.Time `gorm:"index:expired"`
Size uint64
}
// Create 创建容量包
func (pack *StoragePack) Create() (uint, error) {
if err := DB.Create(pack).Error; err != nil {
util.Log().Warning("无法插入离线下载记录, %s", err)
return 0, err
}
return pack.ID, nil
}
// GetAvailablePackSize 返回给定用户当前可用的容量包总容量
func (user *User) GetAvailablePackSize() uint64 {
var (
total uint64
firstExpire *time.Time
timeNow = time.Now()
ttl int64
)
// 尝试从缓存中读取
cacheKey := "pack_size_" + strconv.FormatUint(uint64(user.ID), 10)
if total, ok := cache.Get(cacheKey); ok {
return total.(uint64)
}
// 查找所有有效容量包
packs := user.GetAvailableStoragePacks()
// 计算总容量, 并找到其中最早的过期时间
for _, v := range packs {
total += v.Size
if firstExpire == nil {
firstExpire = v.ExpiredTime
continue
}
if v.ExpiredTime != nil && firstExpire.After(*v.ExpiredTime) {
firstExpire = v.ExpiredTime
}
}
// 用最早的过期时间计算缓存TTL并写入缓存
if firstExpire != nil {
ttl = firstExpire.Unix() - timeNow.Unix()
if ttl > 0 {
_ = cache.Set(cacheKey, total, int(ttl))
}
}
return total
}
// GetAvailableStoragePacks 返回用户可用的容量包
func (user *User) GetAvailableStoragePacks() []StoragePack {
var packs []StoragePack
timeNow := time.Now()
// 查找所有有效容量包
DB.Where("expired_time > ? AND user_id = ?", timeNow, user.ID).Find(&packs)
return packs
}
// GetExpiredStoragePack 获取已过期的容量包
func GetExpiredStoragePack() []StoragePack {
var packs []StoragePack
DB.Where("expired_time < ?", time.Now()).Find(&packs)
return packs
}
// Delete 删除容量包
func (pack *StoragePack) Delete() error {
return DB.Delete(&pack).Error
}

@ -1,86 +0,0 @@
package model
import (
"errors"
"github.com/DATA-DOG/go-sqlmock"
"github.com/jinzhu/gorm"
"github.com/stretchr/testify/assert"
"testing"
"time"
)
func TestUser_GetAvailablePackSize(t *testing.T) {
asserts := assert.New(t)
user := User{
Model: gorm.Model{ID: 1},
}
// 未命中缓存
{
mock.ExpectQuery("SELECT(.+)").
WithArgs(sqlmock.AnyArg(), 1).
WillReturnRows(
sqlmock.NewRows([]string{"id", "size", "expired_time"}).
AddRow(1, 10, time.Now().Add(time.Second*time.Duration(20))).
AddRow(3, 0, nil).
AddRow(2, 20, time.Now().Add(time.Second*time.Duration(10))),
)
total := user.GetAvailablePackSize()
asserts.NoError(mock.ExpectationsWereMet())
asserts.EqualValues(30, total)
}
// 命中缓存
{
total := user.GetAvailablePackSize()
asserts.NoError(mock.ExpectationsWereMet())
asserts.EqualValues(30, total)
}
}
func TestStoragePack_Create(t *testing.T) {
asserts := assert.New(t)
pack := &StoragePack{}
// 成功
{
mock.ExpectBegin()
mock.ExpectExec("INSERT(.+)").WillReturnResult(sqlmock.NewResult(1, 1))
mock.ExpectCommit()
id, err := pack.Create()
asserts.NoError(mock.ExpectationsWereMet())
asserts.Equal(uint(1), id)
asserts.NoError(err)
}
// 失败
{
mock.ExpectBegin()
mock.ExpectExec("INSERT(.+)").WillReturnError(errors.New("error"))
mock.ExpectRollback()
id, err := pack.Create()
asserts.NoError(mock.ExpectationsWereMet())
asserts.Equal(uint(0), id)
asserts.Error(err)
}
}
func TestGetExpiredStoragePack(t *testing.T) {
asserts := assert.New(t)
mock.ExpectQuery("SELECT(.+)").WillReturnRows(sqlmock.NewRows([]string{"id"}))
res := GetExpiredStoragePack()
asserts.NoError(mock.ExpectationsWereMet())
asserts.Len(res, 0)
}
func TestStoragePack_Delete(t *testing.T) {
asserts := assert.New(t)
pack := &StoragePack{}
mock.ExpectBegin()
mock.ExpectExec("UPDATE(.+)").WillReturnResult(sqlmock.NewResult(1, 1))
mock.ExpectCommit()
asserts.NoError(pack.Delete())
asserts.NoError(mock.ExpectationsWereMet())
}

@ -8,7 +8,6 @@ import (
"github.com/jinzhu/gorm"
"github.com/pkg/errors"
"strings"
"time"
)
const (
@ -26,21 +25,16 @@ const (
type User struct {
// 表字段
gorm.Model
Email string `gorm:"type:varchar(100);unique_index"`
Nick string `gorm:"size:50"`
Password string `json:"-"`
Status int
GroupID uint
Storage uint64
OpenID string
TwoFactor string
Avatar string
Options string `json:"-",gorm:"type:text"`
Authn string `gorm:"type:text"`
Score int
PreviousGroupID uint // 初始用户组
GroupExpires *time.Time // 用户组过期日期
NotifyDate *time.Time // 通知超出配额时的日期
Email string `gorm:"type:varchar(100);unique_index"`
Nick string `gorm:"size:50"`
Password string `json:"-"`
Status int
GroupID uint
Storage uint64
TwoFactor string
Avatar string
Options string `json:"-",gorm:"type:text"`
Authn string `gorm:"type:text"`
// 关联模型
Group Group `gorm:"save_associations:false:false"`
@ -52,9 +46,8 @@ type User struct {
// UserOption 用户个性化配置字段
type UserOption struct {
ProfileOff bool `json:"profile_off,omitempty"`
PreferredPolicy uint `json:"preferred_policy,omitempty"`
PreferredTheme string `json:"preferred_theme,omitempty"`
ProfileOff bool `json:"profile_off,omitempty"`
PreferredTheme string `json:"preferred_theme,omitempty"`
}
// Root 获取用户的根目录
@ -94,25 +87,6 @@ func (user *User) IncreaseStorage(size uint64) bool {
return false
}
// PayScore 扣除积分,返回是否成功
func (user *User) PayScore(score int) bool {
if score == 0 {
return true
}
if score <= user.Score {
user.Score -= score
DB.Model(user).Update("score", gorm.Expr("score - ?", score))
return true
}
return false
}
// AddScore 增加积分
func (user *User) AddScore(score int) {
user.Score += score
DB.Model(user).Update("score", gorm.Expr("score + ?", score))
}
// IncreaseStorageWithoutCheck 忽略可用容量,增加用户已用容量
func (user *User) IncreaseStorageWithoutCheck(size uint64) {
if size == 0 {
@ -125,7 +99,7 @@ func (user *User) IncreaseStorageWithoutCheck(size uint64) {
// GetRemainingCapacity 获取剩余配额
func (user *User) GetRemainingCapacity() uint64 {
total := user.Group.MaxStorage + user.GetAvailablePackSize()
total := user.Group.MaxStorage
if total <= user.Storage {
return 0
}
@ -134,26 +108,7 @@ func (user *User) GetRemainingCapacity() uint64 {
// GetPolicyID 获取用户当前的存储策略ID
func (user *User) GetPolicyID(prefer uint) uint {
if prefer == 0 {
prefer = user.OptionsSerialized.PreferredPolicy
}
// 用户未指定时,返回可用的第一个
if prefer == 0 {
if len(user.Group.PolicyList) != 0 {
return user.Group.PolicyList[0]
}
return 1
}
// 用户指定时,先检查是否为可用策略列表中的值
if util.ContainsUint(user.Group.PolicyList, prefer) {
return prefer
}
// 不可用时,返回第一个
if len(user.Group.PolicyList) != 0 {
return user.Group.PolicyList[0]
}
return 1
return user.Group.PolicyList[0]
}
// GetUserByID 用ID获取用户
@ -281,20 +236,6 @@ func (user *User) IsAnonymous() bool {
return user.ID == 0
}
// Notified 更新用户容量超额通知日期
func (user *User) Notified() {
if user.NotifyDate == nil {
timeNow := time.Now()
user.NotifyDate = &timeNow
DB.Model(&user).Update("notify_date", user.NotifyDate)
}
}
// ClearNotified 清除用户通知标记
func (user *User) ClearNotified() {
DB.Model(&user).Update("notify_date", nil)
}
// SetStatus 设定用户状态
func (user *User) SetStatus(status int) {
DB.Model(&user).Update("status", status)
@ -312,41 +253,3 @@ func (user *User) UpdateOptions() error {
}
return user.Update(map[string]interface{}{"options": user.Options})
}
// GetGroupExpiredUsers 获取用户组过期的用户
func GetGroupExpiredUsers() []User {
var users []User
DB.Where("group_expires < ? and previous_group_id <> 0", time.Now()).Find(&users)
return users
}
// GetTolerantExpiredUser 获取超过宽容期的用户
func GetTolerantExpiredUser() []User {
var users []User
DB.Set("gorm:auto_preload", true).Where("notify_date < ?", time.Now().Add(
time.Duration(-GetIntSetting("ban_time", 10))*time.Second),
).Find(&users)
return users
}
// GroupFallback 回退到初始用户组
func (user *User) GroupFallback() {
if user.GroupExpires != nil && user.PreviousGroupID != 0 {
user.Group.ID = user.PreviousGroupID
DB.Model(&user).Updates(map[string]interface{}{
"group_expires": nil,
"previous_group_id": 0,
"group_id": user.PreviousGroupID,
})
}
}
// UpgradeGroup 升级用户组
func (user *User) UpgradeGroup(id uint, expires *time.Time) error {
user.Group.ID = id
return DB.Model(&user).Updates(map[string]interface{}{
"group_expires": expires,
"previous_group_id": user.GroupID,
"group_id": id,
}).Error
}

@ -7,7 +7,7 @@ var BackendVersion = "3.0.0-alpha1"
var RequiredDBVersion = "3.0.0-alpha1"
// IsPro 是否为Pro版本
var IsPro = "true"
var IsPro = "false"
// LastCommit 最后commit id
var LastCommit = "a11f819"

@ -2,7 +2,6 @@ package hashid
import (
"errors"
"github.com/HFO4/cloudreve/bootstrap/constant"
"github.com/HFO4/cloudreve/pkg/conf"
)
import "github.com/speps/go-hashids"
@ -62,7 +61,7 @@ func HashID(id uint, t int) string {
// DecodeHashID 计算HashID对应的数据库ID
func DecodeHashID(id string, t int) (uint, error) {
v, _ := HashDecode(id)
if len(v) != 2 || v[1] != constant.HashIDTable[t] {
if len(v) != 2 || v[1] != t {
return 0, ErrTypeNotMatch
}
return uint(v[0]), nil

@ -1,7 +1,6 @@
package hashid
import (
"github.com/HFO4/cloudreve/bootstrap/constant"
"github.com/stretchr/testify/assert"
"testing"
)
@ -53,7 +52,6 @@ func TestHashDecode(t *testing.T) {
func TestDecodeHashID(t *testing.T) {
asserts := assert.New(t)
constant.HashIDTable = []int{0, 1, 2, 3, 4, 5}
// 成功
{

@ -57,7 +57,6 @@ func GiveGroup(user *model.User, groupInfo *serializer.GroupProducts, num int) e
// GiveScore 积分充值
func GiveScore(user *model.User, num int) error {
user.AddScore(num)
return nil
}

@ -22,17 +22,6 @@ func (pay *ScorePayment) Create(order *model.Order, pack *serializer.PackProduct
return nil, ErrUnsupportedPaymentMethod
}
// 扣除用户积分
if !user.PayScore(order.Price * order.Num) {
return nil, ErrScoreNotEnough
}
// 商品“发货”
if err := GiveProduct(user, pack, group, order.Num); err != nil {
user.AddScore(order.Price * order.Num)
return nil, err
}
// 创建订单记录
order.Status = model.OrderPaid
if _, err := order.Create(); err != nil {

@ -12,8 +12,6 @@ type SiteConfig struct {
QQLogin bool `json:"QQLogin"`
Themes string `json:"themes"`
DefaultTheme string `json:"defaultTheme"`
ScoreEnabled bool `json:"score_enabled"`
ShareScoreRate string `json:"share_score_rate"`
HomepageViewMethod string `json:"home_view_method"`
ShareViewMethod string `json:"share_view_method"`
Authn bool `json:"authn"'`
@ -72,8 +70,6 @@ func BuildSiteConfig(settings map[string]string, user *model.User) Response {
QQLogin: model.IsTrueVal(checkSettingValue(settings, "qq_login")),
Themes: checkSettingValue(settings, "themes"),
DefaultTheme: checkSettingValue(settings, "defaultTheme"),
ScoreEnabled: model.IsTrueVal(checkSettingValue(settings, "score_enabled")),
ShareScoreRate: checkSettingValue(settings, "share_score_rate"),
HomepageViewMethod: checkSettingValue(settings, "home_view_method"),
ShareViewMethod: checkSettingValue(settings, "share_view_method"),
Authn: model.IsTrueVal(checkSettingValue(settings, "authn_enabled")),

@ -11,7 +11,6 @@ type Share struct {
Key string `json:"key"`
Locked bool `json:"locked"`
IsDir bool `json:"is_dir"`
Score int `json:"score"`
CreateDate string `json:"create_date,omitempty"`
Downloads int `json:"downloads"`
Views int `json:"views"`
@ -36,7 +35,6 @@ type shareSource struct {
type myShareItem struct {
Key string `json:"key"`
IsDir bool `json:"is_dir"`
Score int `json:"score"`
Password string `json:"password"`
CreateDate string `json:"create_date,omitempty"`
Downloads int `json:"downloads"`
@ -55,7 +53,6 @@ func BuildShareList(shares []model.Share, total int) Response {
item := myShareItem{
Key: hashid.HashID(shares[i].ID, hashid.ShareID),
IsDir: shares[i].IsDir,
Score: shares[i].Score,
Password: shares[i].Password,
CreateDate: shares[i].CreatedAt.Format("2006-01-02 15:04:05"),
Downloads: shares[i].Downloads,
@ -101,7 +98,6 @@ func BuildShareResponse(share *model.Share, unlocked bool) Share {
Nick: creator.Nick,
GroupName: creator.Group.Name,
},
Score: share.Score,
CreateDate: share.CreatedAt.Format("2006-01-02 15:04:05"),
}

@ -24,7 +24,6 @@ type User struct {
Avatar string `json:"avatar"`
CreatedAt int64 `json:"created_at"`
PreferredTheme string `json:"preferred_theme"`
Score int `json:"score"`
Anonymous bool `json:"anonymous"`
Policy policy `json:"policy"`
Group group `json:"group"`
@ -97,7 +96,6 @@ func BuildUser(user model.User) User {
Avatar: user.Avatar,
CreatedAt: user.CreatedAt.Unix(),
PreferredTheme: user.OptionsSerialized.PreferredTheme,
Score: user.Score,
Anonymous: user.IsAnonymous(),
Policy: policy{
SaveType: user.Policy.Type,
@ -112,7 +110,6 @@ func BuildUser(user model.User) User {
AllowShare: user.Group.ShareEnabled,
AllowRemoteDownload: user.Group.OptionsSerialized.Aria2,
AllowArchiveDownload: user.Group.OptionsSerialized.ArchiveDownload,
ShareFreeEnabled: user.Group.OptionsSerialized.ShareFree,
ShareDownload: user.Group.OptionsSerialized.ShareDownload,
CompressEnabled: user.Group.OptionsSerialized.ArchiveTask,
WebDAVEnabled: user.Group.WebDAVEnabled,

@ -55,25 +55,6 @@ func BuildPolicySettingRes(policies []model.Policy, current *model.Policy) Respo
}
}
// BuildMountedFolderRes 构建已挂载目录响应list为当前用户可用存储策略ID
func BuildMountedFolderRes(folders []model.Folder, list []uint) []MountedFolders {
res := make([]MountedFolders, 0, len(folders))
for _, folder := range folders {
single := MountedFolders{
ID: hashid.HashID(folder.ID, hashid.FolderID),
Name: folder.Name,
PolicyName: "[已失效存储策略]",
}
if policy, err := model.GetPolicyByID(folder.PolicyID); err == nil && util.ContainsUint(list, policy.ID) {
single.PolicyName = policy.Name
}
res = append(res, single)
}
return res
}
// BuildUserQuotaResponse 序列化用户存储配额概况响应
func BuildUserQuotaResponse(user *model.User, packs []model.StoragePack) Response {
packSize := user.GetAvailablePackSize()

@ -356,17 +356,6 @@ func (h *Handler) handlePut(w http.ResponseWriter, r *http.Request, fs *filesyst
fs.Use("AfterValidateFailed", filesystem.HookGiveBackCapacity)
ctx = context.WithValue(ctx, fsctx.FileModelCtx, *originFile)
} else {
// 检查父目录指定存储策略
if exist, folder := fs.IsPathExist(filePath); exist {
if folder.PolicyID != 0 {
// 尝试获取并重设存储策略
if policy, err := model.GetPolicyByID(fs.User.GetPolicyID(folder.PolicyID)); err == nil {
fs.User.Policy = policy
fs.DispatchHandler()
}
}
}
// 给文件系统分配钩子
fs.Use("BeforeUpload", filesystem.HookValidateFile)
fs.Use("BeforeUpload", filesystem.HookValidateCapacity)

@ -21,8 +21,6 @@ func SiteConfig(c *gin.Context) {
"email_active",
"themes",
"defaultTheme",
"score_enabled",
"share_score_rate",
"home_view_method",
"share_view_method",
"authn_enabled",

@ -74,7 +74,6 @@ func InitCORS(router *gin.Engine) {
// InitMasterRouter 初始化主机模式路由
func InitMasterRouter() *gin.Engine {
r := gin.Default()
bootstrap.InitCustomRoute(r.Group("/custom"))
/*

@ -114,7 +114,6 @@ func (service *AddUserService) Add() serializer.Response {
user.Email = service.User.Email
user.GroupID = service.User.GroupID
user.Status = service.User.Status
user.Score = service.User.Score
// 检查愚蠢操作
if user.ID == 1 && user.GroupID != 1 {

@ -115,11 +115,7 @@ func (service *WebDAVAccountService) Delete(c *gin.Context, user *model.User) se
func (service *WebDAVListService) Accounts(c *gin.Context, user *model.User) serializer.Response {
accounts := model.ListWebDAVAccounts(user.ID)
// 查找挂载了存储策略的目录
folders := model.GetMountedFolders(user.ID)
return serializer.Response{Data: map[string]interface{}{
"accounts": accounts,
"folders": serializer.BuildMountedFolderRes(folders, user.Group.PolicyList),
}}
}

@ -16,7 +16,6 @@ type ShareCreateService struct {
Password string `json:"password" binding:"max=255"`
RemainDownloads int `json:"downloads"`
Expire int `json:"expire"`
Score int `json:"score" binding:"gte=0"`
Preview bool `json:"preview"`
}
@ -117,7 +116,6 @@ func (service *ShareCreateService) Create(c *gin.Context) serializer.Response {
IsDir: service.IsDir,
UserID: user.ID,
SourceID: sourceID,
Score: service.Score,
RemainDownloads: -1,
PreviewEnabled: service.Preview,
SourceName: sourceName,

@ -117,8 +117,6 @@ func (service *ShareListService) List(c *gin.Context, user *model.User) serializ
func (service *ShareGetService) Get(c *gin.Context) serializer.Response {
shareCtx, _ := c.Get("share")
share := shareCtx.(*model.Share)
userCtx, _ := c.Get("user")
user := userCtx.(*model.User)
// 是否已解锁
unlocked := true
@ -138,11 +136,6 @@ func (service *ShareGetService) Get(c *gin.Context) serializer.Response {
share.Viewed()
}
// 如果已经下载过或者是自己的分享,不需要付积分
if share.UserID == user.ID || share.WasDownloadedBy(user, c) {
share.Score = 0
}
return serializer.Response{
Code: 0,
Data: serializer.BuildShareResponse(share, unlocked),

Loading…
Cancel
Save