|
|
package model
|
|
|
|
|
|
import (
|
|
|
"crypto/tls"
|
|
|
"errors"
|
|
|
"moredoc/util/captcha"
|
|
|
"moredoc/util/filetil"
|
|
|
"strconv"
|
|
|
"strings"
|
|
|
"time"
|
|
|
|
|
|
jsoniter "github.com/json-iterator/go"
|
|
|
"go.uber.org/zap"
|
|
|
"gopkg.in/gomail.v2"
|
|
|
"gorm.io/gorm"
|
|
|
)
|
|
|
|
|
|
var json = jsoniter.ConfigCompatibleWithStandardLibrary
|
|
|
|
|
|
const (
|
|
|
// ConfigCategorySystem 系统配置:系统名称、logo、版权信息、是否闭站等
|
|
|
ConfigCategorySystem = "system"
|
|
|
// ConfigCategoryUser 用户配置:是否启用注册、是否需要审核等
|
|
|
ConfigCategoryUser = "user"
|
|
|
// ConfigCategoryEmail 邮箱配置:smtp服务器、端口、用户名、密码、发件人
|
|
|
ConfigCategoryEmail = "email"
|
|
|
// ConfigCategoryCaptcha 验证码配置:是否启用验证码、验证码有效期、验证码长度、验证码类型等
|
|
|
ConfigCategoryCaptcha = "captcha"
|
|
|
// ConfigCategorySecurity 安全配置项
|
|
|
ConfigCategorySecurity = "security"
|
|
|
// ConfigCategoryFooter 底部链接
|
|
|
ConfigCategoryFooter = "footer"
|
|
|
// ConfigCategoryConverter 转换配置项
|
|
|
ConfigCategoryConverter = "converter"
|
|
|
// 下载配置
|
|
|
ConfigCategoryDownload = "download"
|
|
|
// 积分规则
|
|
|
ConfigCategoryScore = "score"
|
|
|
// 显示配置
|
|
|
ConfigCategoryDisplay = "display"
|
|
|
)
|
|
|
|
|
|
const (
|
|
|
InputTypeNumber = "number" // 数字
|
|
|
InputTypeSwitch = "switch" // switch
|
|
|
InputTypeSelect = "select" // 单选
|
|
|
InputTypeSelectMulti = "select-multi" // 多选
|
|
|
InputTypeText = "text" // 文本框
|
|
|
InputTypeTextarea = "textarea" // 多行文本框
|
|
|
InputTypePassword = "password" // 密码
|
|
|
InputTypeImage = "image" // 图片
|
|
|
)
|
|
|
|
|
|
type Config struct {
|
|
|
Id int64 `form:"id" json:"id,omitempty" gorm:"primaryKey;autoIncrement;column:id;comment:;"`
|
|
|
Label string `form:"label" json:"label,omitempty" gorm:"column:label;type:varchar(64);size:64;comment:标签名称;"`
|
|
|
Name string `form:"name" json:"name,omitempty" gorm:"column:name;type:varchar(64);size:64;index:name_category,unique;comment:表单字段名称;"`
|
|
|
Value string `form:"value" json:"value,omitempty" gorm:"column:value;type:text;comment:值;"`
|
|
|
Placeholder string `form:"placeholder" json:"placeholder,omitempty" gorm:"column:placeholder;type:varchar(255);size:255;comment:提示信息;"`
|
|
|
InputType string `form:"input_type" json:"input_type,omitempty" gorm:"column:input_type;type:varchar(32);size:32;default:text;comment:输入类型;"`
|
|
|
Category string `form:"category" json:"category,omitempty" gorm:"column:category;type:varchar(32);size:32;index:name_category,unique;index:category;comment:所属类别;"`
|
|
|
Sort int `form:"sort" json:"sort,omitempty" gorm:"column:sort;type:int(11);size:11;default:0;comment:同一category下的排序,这里按顺序排序,值越小越靠前;"`
|
|
|
Options string `form:"options" json:"options,omitempty" gorm:"column:options;type:text;comment:针对checkbox等的枚举值;"`
|
|
|
CreatedAt *time.Time `form:"created_at" json:"created_at,omitempty" gorm:"column:created_at;type:datetime;comment:创建时间;"`
|
|
|
UpdatedAt *time.Time `form:"updated_at" json:"updated_at,omitempty" gorm:"column:updated_at;type:datetime;comment:更新时间;"`
|
|
|
IsSecret bool `form:"is_secret" json:"is_secret,omitempty" gorm:"column:is_secret;type:tinyint(1);size:1;default:0;comment:是否是私密信息;"`
|
|
|
}
|
|
|
|
|
|
func (Config) TableName() string {
|
|
|
return tablePrefix + "config"
|
|
|
}
|
|
|
|
|
|
// CreateConfig 创建Config
|
|
|
func (m *DBModel) CreateConfig(config *Config) (err error) {
|
|
|
err = m.db.Create(config).Error
|
|
|
if err != nil {
|
|
|
m.logger.Error("CreateConfig", zap.Error(err))
|
|
|
return
|
|
|
}
|
|
|
return
|
|
|
}
|
|
|
|
|
|
// UpdateConfig 更新Config,如果需要更新指定字段,则请指定updateFields参数
|
|
|
func (m *DBModel) UpdateConfig(config *Config, updateFields ...string) (err error) {
|
|
|
db := m.db.Model(config)
|
|
|
|
|
|
updateFields = m.FilterValidFields(Config{}.TableName(), updateFields...)
|
|
|
if len(updateFields) > 0 { // 更新指定字段
|
|
|
db = db.Select(updateFields)
|
|
|
}
|
|
|
|
|
|
err = db.Where("id = ?", config.Id).Updates(config).Error
|
|
|
if err != nil {
|
|
|
m.logger.Error("UpdateConfig", zap.Error(err))
|
|
|
}
|
|
|
return
|
|
|
}
|
|
|
|
|
|
// UpdateConfigs 配置项批量更新
|
|
|
func (m *DBModel) UpdateConfigs(configs []*Config, updateFields ...string) (err error) {
|
|
|
sess := m.db.Begin()
|
|
|
defer func() {
|
|
|
if err != nil {
|
|
|
sess.Rollback()
|
|
|
} else {
|
|
|
sess.Commit()
|
|
|
}
|
|
|
}()
|
|
|
|
|
|
tableName := Config{}.TableName()
|
|
|
updateFields = m.FilterValidFields(tableName, updateFields...)
|
|
|
if len(updateFields) == 0 {
|
|
|
updateFields = m.GetTableFields(tableName)
|
|
|
}
|
|
|
|
|
|
for _, config := range configs {
|
|
|
m.logger.Debug("UpdateConfigs", zap.Any("config", config), zap.Any("updateFields", updateFields))
|
|
|
if err = sess.Select(updateFields).Updates(config).Error; err != nil {
|
|
|
m.logger.Error("UpdateConfigs", zap.Error(err))
|
|
|
return
|
|
|
}
|
|
|
}
|
|
|
return
|
|
|
}
|
|
|
|
|
|
// GetConfig 根据id获取Config
|
|
|
func (m *DBModel) GetConfig(id interface{}, fields ...string) (config Config, err error) {
|
|
|
db := m.db
|
|
|
|
|
|
fields = m.FilterValidFields(Config{}.TableName(), fields...)
|
|
|
if len(fields) > 0 {
|
|
|
db = db.Select(fields)
|
|
|
}
|
|
|
|
|
|
err = db.Where("id = ?", id).First(&config).Error
|
|
|
return
|
|
|
}
|
|
|
|
|
|
// GetConfigByNameCategory(name string, category string, fields ...string) 根据唯一索引获取Config
|
|
|
func (m *DBModel) GetConfigByNameCategory(name string, category string, fields ...string) (config Config, err error) {
|
|
|
db := m.db
|
|
|
|
|
|
fields = m.FilterValidFields(Config{}.TableName(), fields...)
|
|
|
if len(fields) > 0 {
|
|
|
db = db.Select(fields)
|
|
|
}
|
|
|
|
|
|
db = db.Where("name = ?", name)
|
|
|
|
|
|
db = db.Where("category = ?", category)
|
|
|
|
|
|
err = db.First(&config).Error
|
|
|
if err != nil && err != gorm.ErrRecordNotFound {
|
|
|
m.logger.Error("GetConfigByNameCategory", zap.Error(err))
|
|
|
return
|
|
|
}
|
|
|
return
|
|
|
}
|
|
|
|
|
|
type OptionGetConfigList struct {
|
|
|
SelectFields []string // 查询字段
|
|
|
QueryIn map[string][]interface{} // map[field][]{value1,value2,...}
|
|
|
}
|
|
|
|
|
|
// GetConfigList 获取Config列表
|
|
|
func (m *DBModel) GetConfigList(opt *OptionGetConfigList) (configList []Config, err error) {
|
|
|
db := m.db.Model(&Config{})
|
|
|
db = m.generateQueryIn(db, Config{}.TableName(), opt.QueryIn)
|
|
|
err = db.Order("sort asc").Find(&configList).Error
|
|
|
if err != nil && err != gorm.ErrRecordNotFound {
|
|
|
m.logger.Error("GetConfigList", zap.Error(err))
|
|
|
}
|
|
|
return
|
|
|
}
|
|
|
|
|
|
// DeleteConfig 删除数据
|
|
|
func (m *DBModel) DeleteConfig(ids []interface{}) (err error) {
|
|
|
err = m.db.Where("id in (?)", ids).Delete(&Config{}).Error
|
|
|
if err != nil {
|
|
|
m.logger.Error("DeleteConfig", zap.Error(err))
|
|
|
}
|
|
|
return
|
|
|
}
|
|
|
|
|
|
const (
|
|
|
ConfigCaptchaLength = "length"
|
|
|
ConfigCaptchaWidth = "width"
|
|
|
ConfigCaptchaHeight = "height"
|
|
|
ConfigCaptchaType = "type"
|
|
|
)
|
|
|
|
|
|
type ConfigCaptcha struct {
|
|
|
Length int `json:"length"` // 验证码长度
|
|
|
Width int `json:"width"`
|
|
|
Height int `json:"height"`
|
|
|
Type string `json:"type"` // 验证码类型
|
|
|
}
|
|
|
|
|
|
const (
|
|
|
ConfigSystemSitename = "sitename"
|
|
|
ConfigSystemDomain = "domain"
|
|
|
ConfigSystemTitle = "title"
|
|
|
ConfigSystemDescription = "description"
|
|
|
ConfigSystemKeywords = "keywords"
|
|
|
ConfigSystemRecommendWords = "recommend_words"
|
|
|
ConfigSystemLogo = "logo"
|
|
|
ConfigSystemFavicon = "favicon"
|
|
|
ConfigSystemLoginBackground = "login_background"
|
|
|
ConfigSystemRegistrerBackground = "register_background"
|
|
|
ConfigSystemIcp = "icp"
|
|
|
ConfigSystemSecIcp = "sec_icp"
|
|
|
ConfigSystemAnalytics = "analytics"
|
|
|
ConfigSystemCopyrightStartYear = "copyright_start_year"
|
|
|
)
|
|
|
|
|
|
type ConfigSystem struct {
|
|
|
Sitename string `json:"sitename"` // 网站名称
|
|
|
Domain string `json:"domain"` // 站点域名,带 HTTPS:// 或 HTTP://
|
|
|
Title string `json:"title"` // 网站首页标题
|
|
|
Keywords string `json:"keywords"` // 系统关键字
|
|
|
Description string `json:"description"` // 系统描述
|
|
|
Logo string `json:"logo"` // logo
|
|
|
Favicon string `json:"favicon"` // favicon
|
|
|
ConfigSystemRegistrerBackground string `json:"register_background"` // 注册页面背景图
|
|
|
ConfigSystemLoginBackground string `json:"login_background"` // 登录页面背景图
|
|
|
SecICP string `json:"sec_icp"` // 网站备案
|
|
|
ICP string `json:"icp"` // 网站备案
|
|
|
Analytics string `json:"analytics"` // 统计代码
|
|
|
CopyrightStartYear string `json:"copyright_start_year"` // 版权年
|
|
|
RecommendWords []string `json:"recommend_words"` // 推荐词,首页收缩推荐词
|
|
|
}
|
|
|
|
|
|
const (
|
|
|
ConfigSecurityMaxDocumentSize = "max_document_size" // 是否关闭注册
|
|
|
ConfigSecurityCommentInterval = "comment_interval" // 评论时间间隔
|
|
|
ConfigSecurityIsClose = "is_close" // 是否关闭注册
|
|
|
ConfigSecurityCloseStatement = "close_statement" // 闭站说明
|
|
|
ConfigSecurityEnableRegister = "enable_register" // 是否允许注册
|
|
|
ConfigSecurityEnableCaptchaLogin = "enable_captcha_login" // 是否开启登录验证码
|
|
|
ConfigSecurityEnableCaptchaRegister = "enable_captcha_register" // 是否开启注册验证码
|
|
|
ConfigSecurityEnableCaptchaComment = "enable_captcha_comment" // 是否开启注册验证码
|
|
|
ConfigSecurityEnableCaptchaFindPassword = "enable_captcha_find_password" // 是否开启注册验证码
|
|
|
ConfigSecurityDocumentRelatedDuration = "document_related_duration" // 相关文档有效期,默认为7天,最小值为1
|
|
|
ConfigSecurityDocumentAllowedExt = "document_allowed_ext" // 允许上传的文档类型
|
|
|
ConfigSecurityLoginRequired = "login_required" // 是否需要登录才能查看文档
|
|
|
ConfigSecurityAttachmentRetentionMinute = "attachment_retention_minute" // 被删除的附件,保留时长,单位分钟
|
|
|
ConfigSecurityEnableVerifyRegisterEmail = "enable_verify_register_email" // 是否开启注册邮箱验证
|
|
|
)
|
|
|
|
|
|
type ConfigSecurity struct {
|
|
|
MaxDocumentSize int32 `json:"max_document_size"` // 允许上传的最大文档大小
|
|
|
CommentInterval int32 `json:"comment_interval"` // 评论时间间隔, 单位秒
|
|
|
DocumentRelatedDuration int32 `json:"document_related_duration"` // 相关文档有效期,默认为7天,最小值为1
|
|
|
AttachmentRetentionMinute int32 `json:"attachment_retention_minute"` // 被删除的附件,保留时长,单位分钟
|
|
|
IsClose bool `json:"is_close"` // 是否闭站
|
|
|
CloseStatement string `json:"close_statement"` // 闭站说明
|
|
|
EnableRegister bool `json:"enable_register"` // 是否启用注册
|
|
|
EnableCaptchaLogin bool `json:"enable_captcha_login"` // 是否启用登录验证码
|
|
|
EnableCaptchaRegister bool `json:"enable_captcha_register"` // 是否启用注册验证码
|
|
|
EnableCaptchaComment bool `json:"enable_captcha_comment"` // 是否启用评论验证码
|
|
|
EnableCaptchaFindPassword bool `json:"enable_captcha_find_password"` // 找回密码是否需要验证码
|
|
|
EnableVerifyRegisterEmail bool `json:"enable_verify_register_email"` // 是否开启注册邮箱验证
|
|
|
DocumentAllowedExt []string `json:"document_allowed_ext"` // 允许上传的文档类型
|
|
|
LoginRequired bool `json:"login_required"` // 是否需要登录才能查看文档
|
|
|
}
|
|
|
|
|
|
const (
|
|
|
ConfigConverterMaxPreview = "max_preview" // 最大预览页数
|
|
|
ConfigConverterTimeout = "timeout" // 转换超时时间
|
|
|
ConfigConverterEnableSVGO = "enable_svgo" // 是否启用 SVGO
|
|
|
ConfigConverterEnableGZIP = "enable_gzip" // 是否启用 GZIP
|
|
|
ConfigConverterEnableConvertRepeatedDocument = "enable_convert_repeated_document" // 是否转换已转换的重复文档
|
|
|
ConfigConverterExtension = "extension" // 转换后的文件扩展名,即以什么方式来浏览文档,支持 jpg、png、svg,默认为svg,pdf和html暂不支持
|
|
|
)
|
|
|
|
|
|
const (
|
|
|
// 是否允许游客下载
|
|
|
ConfigDownloadEnableGuestDownload = "enable_guest_download"
|
|
|
// 每天允许下载的次数
|
|
|
ConfigDownloadTimesEveryDay = "times_every_day"
|
|
|
// 下载链接地址签名密钥
|
|
|
ConfigDownloadSecretKey = "secret_key"
|
|
|
// 生成的下载链接有效期,单位为秒
|
|
|
ConfigDownloadUrlDuration = "url_duration"
|
|
|
// 购买文档后多少天内允许免费重复下载
|
|
|
ConfigDownloadFreeDownloadDuration = "free_download_duration"
|
|
|
// 允许每个IP每天下载的次数
|
|
|
ConfigDownloadTimesEveryIP = "times_every_ip"
|
|
|
)
|
|
|
|
|
|
type ConfigDownload struct {
|
|
|
EnableGuestDownload bool `json:"enable_guest_download"` // 是否允许游客下载(不需要登录,针对免费文档)
|
|
|
TimesEveryDay int32 `json:"times_every_day"` // 允许用户每天下载的次数(针对登录用户)
|
|
|
TimesEveryIP int32 `json:"times_every_ip"` // 允许每个IP每天下载的次数(针对所有用户)
|
|
|
SecretKey string `json:"secret_key"` // 下载链接地址签名密钥
|
|
|
UrlDuration int32 `json:"url_duration"` // 生成的下载链接有效期,单位为秒
|
|
|
FreeDownloadDuration int32 `json:"free_download_duration"` // 购买文档后多少天内允许免费重复下载
|
|
|
}
|
|
|
|
|
|
// ConfigConverter 转换配置
|
|
|
type ConfigConverter struct {
|
|
|
MaxPreview int `json:"max_preview"` // 文档所允许的最大预览页数,0 表示不限制,全部转换
|
|
|
Timeout int `json:"timeout"` // 转换超时时间,单位为分钟,默认30分钟
|
|
|
EnableSVGO bool `json:"enable_svgo"` // 是否对svg启用SVGO压缩。转换效率会有所下降。相对直接的svg文件,可以节省1/2的存储空间
|
|
|
EnableGZIP bool `json:"enable_gzip"` // 是否对svg启用GZIP压缩。转换效率会有所下降。相对直接的svg文件,可以节省3/4的存储空间
|
|
|
EnableConvertRepeatedDocument bool `json:"enable_convert_repeated_document"` // 是否转换已转换的重复文档。如果开启,会导致转换效率下降,但是可以节省大量的存储空间
|
|
|
// GZIP和svgo都开启,转换效率会有所下降,可以综合节省约85%的存储空间
|
|
|
Extension string `json:"extension"` // 转换后的文件扩展名,即以什么方式来浏览文档,支持 jpg、png、svg,默认为svg,pdf和html暂不支持
|
|
|
}
|
|
|
|
|
|
const (
|
|
|
ConfigEmailEnable = "enable" // 是否启用邮件服务
|
|
|
ConfigEmailHost = "host" // SMTP 服务器地址
|
|
|
ConfigEmailPort = "port" // SMTP 服务器端口
|
|
|
ConfigEmailIsTLS = "is_tls" // 是否启用TLS
|
|
|
ConfigEmailFromName = "from_name"
|
|
|
ConfigEmailUsername = "username" // SMTP 用户名
|
|
|
ConfigEmailPassword = "password" // SMTP 密码
|
|
|
ConfigEmailDuration = "duration" // 验证码有效期,单位为分钟
|
|
|
ConfigEmailTestEmail = "test_email"
|
|
|
ConfigEmailReplyTo = "reply_to"
|
|
|
ConfigEmailSecret = "secret" // 找回密码邮件的签名密钥
|
|
|
)
|
|
|
|
|
|
type ConfigEmail struct {
|
|
|
Enable bool `json:"enable"` // 是否启用邮件服务
|
|
|
Host string `json:"host"` // SMTP 服务器地址
|
|
|
Port int `json:"port"` // SMTP 服务器端口
|
|
|
IsTLS bool `json:"is_tls"` // 是否启用TLS
|
|
|
FromName string `json:"from_name"`
|
|
|
Username string `json:"username"`
|
|
|
Password string `json:"password"`
|
|
|
Duration int `json:"duration"` // 验证码有效期,单位为分钟
|
|
|
TestEmail string `json:"test_email"`
|
|
|
Secret string `json:"secret"`
|
|
|
// ReplyTo string `json:"reply_to"`
|
|
|
}
|
|
|
|
|
|
const (
|
|
|
ConfigFooterAbout = "about" // 关于我们
|
|
|
ConfigFooterContact = "contact" // 联系我们
|
|
|
ConfigFooterAgreement = "agreement" // 用户协议
|
|
|
ConfigFooterCopyright = "copyright" // 版权信息
|
|
|
ConfigFooterFeedback = "feedback" // 反馈信息
|
|
|
)
|
|
|
|
|
|
type ConfigFooter struct {
|
|
|
About string `json:"about"` // 关于我们
|
|
|
Contact string `json:"contact"` // 联系我们
|
|
|
Agreement string `json:"agreement"` // 用户协议、文库协议
|
|
|
Copyright string `json:"copyright"` // 版权信息、免责声明
|
|
|
Feedback string `json:"feedback"` // 反馈
|
|
|
}
|
|
|
|
|
|
const (
|
|
|
// 积分名称
|
|
|
ConfigScoreCreditName = "credit_name"
|
|
|
// 注册积分
|
|
|
ConfigScoreRegister = "register"
|
|
|
// 签到积分
|
|
|
ConfigScoreSignIn = "sign_in"
|
|
|
// 上传文档积分
|
|
|
ConfigScoreUploadDocument = "upload_document"
|
|
|
// 每日上传文档积分次数限制
|
|
|
ConfigScoreUploadDocumentLimit = "upload_document_limit"
|
|
|
// 删除上传文档积分
|
|
|
ConfigScoreDeleteDocument = "delete_document"
|
|
|
// 文档被收藏获得积分
|
|
|
ConfigScoreDocumentCollected = "document_collected"
|
|
|
// 文档被收藏获得积分次数限制
|
|
|
ConfigScoreDocumentCollectedLimit = "document_collected_limit"
|
|
|
// 文档被评论获得积分
|
|
|
ConfigScoreDocumentCommented = "document_commented"
|
|
|
// 文档被评论获得积分次数限制
|
|
|
ConfigScoreDocumentCommentedLimit = "document_commented_limit"
|
|
|
)
|
|
|
|
|
|
type ConfigScore struct {
|
|
|
CreditName string `json:"credit_name"` // 积分名称
|
|
|
Register int32 `json:"register"` // 注册积分
|
|
|
SignIn int32 `json:"sign_in"` // 签到积分
|
|
|
UploadDocument int32 `json:"upload_document"` // 上传文档积分
|
|
|
UploadDocumentLimit int32 `json:"upload_document_limit"` // 每日上传文档积分次数限制
|
|
|
DeleteDocument int32 `json:"delete_document"` // 删除上传文档积分
|
|
|
DocumentCollected int32 `json:"document_collected"` // 文档被收藏获得积分
|
|
|
DocumentCollectedLimit int32 `json:"document_collected_limit"` // 文档被收藏获得积分次数限制
|
|
|
DocumentCommented int32 `json:"document_commented"` // 文档被评论获得积分
|
|
|
DocumentCommentedLimit int32 `json:"document_commented_limit"` // 文档被评论获得积分次数限制
|
|
|
}
|
|
|
|
|
|
const (
|
|
|
ConfigDisplayShowRegisterUserCount = "show_register_user_count" // 是否显示注册用户数量
|
|
|
ConfigDisplayShowDocumentDescriptions = "show_document_descriptions" // 是否在文档页显示文档摘要
|
|
|
ConfigDisplayVirtualRegisterCount = "virtual_register_count" // 虚拟注册用户数量
|
|
|
ConfigDisplayShowIndexCategories = "show_index_categories" // 是否显示首页分类
|
|
|
ConfigDisplayPagesPerRead = "pages_per_read" // 每次阅读的页数
|
|
|
ConfigDisplayCopyrightStatement = "copyright_statement" // 在页面最底部的版权声明
|
|
|
ConfigDisplayMaxSearchPages = "max_search_pages" // 搜索结果最大页数
|
|
|
)
|
|
|
|
|
|
type ConfigDisplay struct {
|
|
|
ShowRegisterUserCount bool `json:"show_register_user_count"` // 是否显示注册用户数量
|
|
|
ShowDocumentDescriptions bool `json:"show_document_descriptions"` // 是否在文档页显示文档摘要
|
|
|
VirtualRegisterCount int64 `json:"virtual_register_count"` // 虚拟注册用户数量
|
|
|
ShowIndexCategories bool `json:"show_index_categories"` // 是否显示首页分类
|
|
|
PagesPerRead int32 `json:"pages_per_read"` // 每次阅读的页数
|
|
|
MaxSearchPages int32 `json:"max_search_pages"` // 搜索结果最大页数
|
|
|
CopyrightStatement string `json:"copyright_statement"` // 在页面最底部的版权声明
|
|
|
}
|
|
|
|
|
|
func (m *DBModel) GetConfigOfDisplay(name ...string) (config ConfigDisplay) {
|
|
|
var configs []Config
|
|
|
|
|
|
db := m.db
|
|
|
if len(name) > 0 {
|
|
|
db = db.Where("name IN (?)", name)
|
|
|
}
|
|
|
err := db.Where("category = ?", ConfigCategoryDisplay).Find(&configs).Error
|
|
|
if err != nil && err != gorm.ErrRecordNotFound {
|
|
|
m.logger.Error("GetConfigOfDisplay", zap.Error(err))
|
|
|
}
|
|
|
|
|
|
data := m.convertConfig2Map(configs)
|
|
|
|
|
|
bytes, _ := json.Marshal(data)
|
|
|
json.Unmarshal(bytes, &config)
|
|
|
|
|
|
return
|
|
|
}
|
|
|
|
|
|
func (m *DBModel) GetConfigOfFooter() (config ConfigFooter) {
|
|
|
var configs []Config
|
|
|
err := m.db.Where("category = ?", ConfigCategoryFooter).Find(&configs).Error
|
|
|
if err != nil && err != gorm.ErrRecordNotFound {
|
|
|
m.logger.Error("GetConfigOfFooter", zap.Error(err))
|
|
|
}
|
|
|
|
|
|
data := m.convertConfig2Map(configs)
|
|
|
|
|
|
bytes, _ := json.Marshal(data)
|
|
|
json.Unmarshal(bytes, &config)
|
|
|
|
|
|
return
|
|
|
}
|
|
|
|
|
|
func (m *DBModel) GetConfigOfDownload(name ...string) (config ConfigDownload) {
|
|
|
var configs []Config
|
|
|
|
|
|
db := m.db
|
|
|
if len(name) > 0 {
|
|
|
db = db.Where("name IN (?)", name)
|
|
|
}
|
|
|
err := db.Where("category = ?", ConfigCategoryDownload).Find(&configs).Error
|
|
|
if err != nil && err != gorm.ErrRecordNotFound {
|
|
|
m.logger.Error("GetConfigOfDownload", zap.Error(err))
|
|
|
}
|
|
|
|
|
|
data := m.convertConfig2Map(configs)
|
|
|
bytes, _ := json.Marshal(data)
|
|
|
json.Unmarshal(bytes, &config)
|
|
|
|
|
|
return
|
|
|
}
|
|
|
|
|
|
// GetConfigOfCaptcha 获取验证码配置
|
|
|
func (m *DBModel) GetConfigOfCaptcha() (config ConfigCaptcha) {
|
|
|
var configs []Config
|
|
|
err := m.db.Where("category = ?", ConfigCategoryCaptcha).Find(&configs).Error
|
|
|
if err != nil && err != gorm.ErrRecordNotFound {
|
|
|
m.logger.Error("GetConfigOfCaptcha", zap.Error(err))
|
|
|
}
|
|
|
data := m.convertConfig2Map(configs)
|
|
|
bytes, _ := json.Marshal(data)
|
|
|
json.Unmarshal(bytes, &config)
|
|
|
|
|
|
if config.Length <= 0 {
|
|
|
config.Length = 6
|
|
|
}
|
|
|
if config.Width <= 0 {
|
|
|
config.Width = 240
|
|
|
}
|
|
|
if config.Height <= 0 {
|
|
|
config.Height = 60
|
|
|
}
|
|
|
return
|
|
|
}
|
|
|
|
|
|
// GetConfigOfSystem 获取系统配置
|
|
|
func (m *DBModel) GetConfigOfSystem(name ...string) (config ConfigSystem) {
|
|
|
var confgis []Config
|
|
|
db := m.db.Where("category = ?", ConfigCategorySystem)
|
|
|
if len(name) > 0 {
|
|
|
db = db.Where("name IN (?)", name)
|
|
|
}
|
|
|
err := db.Find(&confgis).Error
|
|
|
if err != nil && err != gorm.ErrRecordNotFound {
|
|
|
m.logger.Error("GetConfigOfSystem", zap.Error(err))
|
|
|
}
|
|
|
|
|
|
data := m.convertConfig2Map(confgis)
|
|
|
// 注意:推荐的关键字,要特殊处理下
|
|
|
if recommendWords, ok := data[ConfigSystemRecommendWords]; ok {
|
|
|
var words []string
|
|
|
if rws, ok := recommendWords.(string); ok {
|
|
|
for _, word := range strings.Split(rws, ",") {
|
|
|
word = strings.TrimSpace(word)
|
|
|
if word == "" {
|
|
|
continue
|
|
|
}
|
|
|
words = append(words, word)
|
|
|
}
|
|
|
}
|
|
|
data[ConfigSystemRecommendWords] = words
|
|
|
}
|
|
|
bytes, _ := json.Marshal(data)
|
|
|
json.Unmarshal(bytes, &config)
|
|
|
return
|
|
|
}
|
|
|
|
|
|
// GetConfigOfSecurity 获取安全配置
|
|
|
func (m *DBModel) GetConfigOfSecurity(name ...string) (config ConfigSecurity) {
|
|
|
var configs []Config
|
|
|
db := m.db.Where("category = ?", ConfigCategorySecurity)
|
|
|
if len(name) > 0 {
|
|
|
db = db.Where("name in (?)", name)
|
|
|
}
|
|
|
err := db.Find(&configs).Error
|
|
|
if err != nil && err != gorm.ErrRecordNotFound {
|
|
|
m.logger.Error("GetConfigOfSecurity", zap.Error(err))
|
|
|
}
|
|
|
|
|
|
data := m.convertConfig2Map(configs)
|
|
|
bytes, _ := json.Marshal(data)
|
|
|
json.Unmarshal(bytes, &config)
|
|
|
|
|
|
if config.MaxDocumentSize <= 0 {
|
|
|
config.MaxDocumentSize = 50 // 默认50M
|
|
|
}
|
|
|
|
|
|
return
|
|
|
}
|
|
|
|
|
|
func (m *DBModel) GetConfigOfConverter() (config ConfigConverter) {
|
|
|
var configs []Config
|
|
|
err := m.db.Where("category = ?", ConfigCategoryConverter).Find(&configs).Error
|
|
|
if err != nil && err != gorm.ErrRecordNotFound {
|
|
|
m.logger.Error("GetConfigOfConverter", zap.Error(err))
|
|
|
}
|
|
|
|
|
|
data := m.convertConfig2Map(configs)
|
|
|
bytes, _ := json.Marshal(data)
|
|
|
json.Unmarshal(bytes, &config)
|
|
|
|
|
|
return
|
|
|
}
|
|
|
|
|
|
func (m *DBModel) GetConfigOfEmail(name ...string) (config ConfigEmail) {
|
|
|
var configs []Config
|
|
|
db := m.db.Where("category = ?", ConfigCategoryEmail)
|
|
|
if len(name) > 0 {
|
|
|
db = db.Where("name in (?)", name)
|
|
|
}
|
|
|
err := db.Find(&configs).Error
|
|
|
if err != nil && err != gorm.ErrRecordNotFound {
|
|
|
m.logger.Error("GetConfigOfEmail", zap.Error(err))
|
|
|
}
|
|
|
|
|
|
data := m.convertConfig2Map(configs)
|
|
|
bytes, _ := json.Marshal(data)
|
|
|
json.Unmarshal(bytes, &config)
|
|
|
m.logger.Debug("GetConfigOfEmail", zap.Any("data", data), zap.Any("config", config))
|
|
|
|
|
|
return
|
|
|
}
|
|
|
|
|
|
func (m *DBModel) GetConfigOfScore(name ...string) (config ConfigScore) {
|
|
|
var configs []Config
|
|
|
db := m.db.Where("category = ?", ConfigCategoryScore)
|
|
|
if len(name) > 0 {
|
|
|
db = db.Where("name in (?)", name)
|
|
|
}
|
|
|
err := db.Find(&configs).Error
|
|
|
if err != nil && err != gorm.ErrRecordNotFound {
|
|
|
m.logger.Error("GetConfigOfScore", zap.Error(err))
|
|
|
}
|
|
|
|
|
|
data := m.convertConfig2Map(configs)
|
|
|
bytes, _ := json.Marshal(data)
|
|
|
json.Unmarshal(bytes, &config)
|
|
|
|
|
|
return
|
|
|
}
|
|
|
|
|
|
func (m *DBModel) SendMail(subject, email string, body string) error {
|
|
|
cfg := m.GetConfigOfEmail()
|
|
|
m.logger.Debug("SendMail", zap.Any("cfg", cfg), zap.String("email", email), zap.String("subject", subject), zap.String("body", body))
|
|
|
if !cfg.Enable {
|
|
|
return errors.New("邮件服务未启用")
|
|
|
}
|
|
|
fromName := cfg.Username
|
|
|
if fn := strings.TrimSpace(cfg.FromName); fn != "" {
|
|
|
fromName = fn
|
|
|
}
|
|
|
message := gomail.NewMessage()
|
|
|
message.SetHeader("From", message.FormatAddress(cfg.Username, fromName))
|
|
|
message.SetHeader("To", email)
|
|
|
message.SetHeader("Subject", subject)
|
|
|
message.SetBody("text/html", body)
|
|
|
// if cfg.ReplyTo != "" {
|
|
|
// message.SetHeader("Reply-To", cfg.ReplyTo)
|
|
|
// }
|
|
|
|
|
|
mail := gomail.NewDialer(cfg.Host, cfg.Port, cfg.Username, cfg.Password)
|
|
|
if cfg.IsTLS {
|
|
|
mail.TLSConfig = &tls.Config{InsecureSkipVerify: true}
|
|
|
}
|
|
|
return mail.DialAndSend(message)
|
|
|
}
|
|
|
func (m *DBModel) GetCreditName() string {
|
|
|
creditName := m.GetConfigOfScore(ConfigScoreCreditName).CreditName
|
|
|
if creditName == "" {
|
|
|
creditName = "魔豆"
|
|
|
}
|
|
|
return creditName
|
|
|
}
|
|
|
|
|
|
func (m *DBModel) convertConfig2Map(cfgs []Config) (data map[string]interface{}) {
|
|
|
data = make(map[string]interface{})
|
|
|
for _, cfg := range cfgs {
|
|
|
switch strings.TrimSpace(cfg.InputType) {
|
|
|
case InputTypeNumber:
|
|
|
data[cfg.Name], _ = strconv.Atoi(cfg.Value)
|
|
|
case InputTypeSwitch:
|
|
|
data[cfg.Name], _ = strconv.ParseBool(cfg.Value)
|
|
|
case InputTypeSelectMulti:
|
|
|
arr := strings.Split(cfg.Value, ",")
|
|
|
if len(arr) == 1 && arr[0] == "" {
|
|
|
arr = []string{}
|
|
|
}
|
|
|
data[cfg.Name] = arr
|
|
|
case InputTypeSelect, InputTypeText, InputTypeTextarea, InputTypePassword, InputTypeImage:
|
|
|
data[cfg.Name] = cfg.Value
|
|
|
default:
|
|
|
// 这里只是做一个兼容处理。报错是为了提醒开发者,如果有新的输入类型,需要增加 InptType 的枚举常量,并在相应的表单进行处理
|
|
|
m.logger.Error("convertConfig2Map", zap.Error(errors.New("未知的输入类型,请在枚举常量InputType中进行定义")), zap.String("inputType", cfg.InputType), zap.String("name", cfg.Name), zap.String("value", cfg.Value))
|
|
|
data[cfg.Name] = cfg.Value
|
|
|
}
|
|
|
}
|
|
|
return
|
|
|
}
|
|
|
|
|
|
func (m *DBModel) initConfig() (err error) {
|
|
|
closeStatement := `<div>尊敬的用户,您好:</div>
|
|
|
<div>为了给您带来更好的使用体验,<strong>魔豆文库</strong> 正在对服务进行升级维护,预计恢复时间为 <span style="color:red">2024-10-24 06:00:00</span>,请您稍后再进行访问。</div>
|
|
|
<div>升级维护期间,普通用户将无法正常使用(系统管理人员出于维护的需要不受升级影响)。</div>
|
|
|
<div>由此带来的不便,敬请谅解。</div>`
|
|
|
// 初始化配置项
|
|
|
cfgs := []Config{
|
|
|
// 系统配置项
|
|
|
{Category: ConfigCategorySystem, Name: ConfigSystemSitename, Label: "网站名称", Value: "魔豆文库", Placeholder: "请输入您网站的名称,如:魔豆文库", InputType: InputTypeText, Sort: 10, Options: ""},
|
|
|
{Category: ConfigCategorySystem, Name: ConfigSystemDomain, Label: "【重要】网站域名", Value: "https://moredoc.mnt.ltd", Placeholder: "请输入您网站的域名访问地址,带 https:// 或 http:// 如 https://moredoc.mnt.ltd,用以生成网站地图sitemap", InputType: InputTypeText, Sort: 11, Options: ""},
|
|
|
{Category: ConfigCategorySystem, Name: ConfigSystemTitle, Label: "首页标题", Value: "MOREDOC · 魔豆文库", Placeholder: "请输入您网站的首页标题,如:魔豆文库,强大、专业的文库系统", InputType: InputTypeText, Sort: 20, Options: ""},
|
|
|
{Category: ConfigCategorySystem, Name: ConfigSystemKeywords, Label: "网站关键字", Value: "MOREDOC · 魔豆文库", Placeholder: "请输入您网站的关键字", InputType: InputTypeText, Sort: 30, Options: ""},
|
|
|
{Category: ConfigCategorySystem, Name: ConfigSystemDescription, Label: "网站描述", Value: "MOREDOC · 魔豆文库", Placeholder: "请输入您网站的描述", InputType: InputTypeTextarea, Sort: 40, Options: ""},
|
|
|
{Category: ConfigCategorySystem, Name: ConfigSystemRecommendWords, Label: "首页搜索推荐词", Value: "", Placeholder: "网站首页搜索推荐关键字,多个关键字用英文逗号分隔", InputType: InputTypeText, Sort: 50, Options: ""},
|
|
|
{Category: ConfigCategorySystem, Name: ConfigSystemLogo, Label: "网站Logo", Value: "", Placeholder: "请上传一张图片作为网站Logo", InputType: InputTypeImage, Sort: 60, Options: ""},
|
|
|
{Category: ConfigCategorySystem, Name: ConfigSystemFavicon, Label: "网站Favicon", Value: "", Placeholder: "请上传一张方方正正的小图片作为网站favicon,建议为 .ico 的图片", InputType: InputTypeImage, Sort: 61, Options: ""},
|
|
|
{Category: ConfigCategorySystem, Name: ConfigSystemRegistrerBackground, Label: "注册页背景图", Value: "", Placeholder: "请上传一张图片作为注册页背景图", InputType: InputTypeImage, Sort: 62, Options: ""},
|
|
|
{Category: ConfigCategorySystem, Name: ConfigSystemLoginBackground, Label: "登录页背景图", Value: "", Placeholder: "请上传一张图片作为登录页背景图", InputType: InputTypeImage, Sort: 63, Options: ""},
|
|
|
{Category: ConfigCategorySystem, Name: ConfigSystemSecIcp, Label: "京公网安备", Value: "", Placeholder: "请输入您网站的京公网安备备案号", InputType: InputTypeText, Sort: 68, Options: ""},
|
|
|
{Category: ConfigCategorySystem, Name: ConfigSystemIcp, Label: "网站备案号", Value: "", Placeholder: "请输入您网站的备案号", InputType: InputTypeText, Sort: 69, Options: ""},
|
|
|
{Category: ConfigCategorySystem, Name: ConfigSystemCopyrightStartYear, Label: "版权起始年", Value: "2019", Placeholder: "请输入您网站版权起始年,如:2019,则前台会显示如 ©2019 - 20xx 的字样", InputType: InputTypeText, Sort: 80, Options: ""},
|
|
|
{Category: ConfigCategorySystem, Name: ConfigSystemAnalytics, Label: "网站统计代码", Value: "", Placeholder: "请输入您网站的统计代码,当前只支持百度统计", InputType: InputTypeTextarea, Sort: 90, Options: ""},
|
|
|
|
|
|
// 验证码配置项
|
|
|
{Category: ConfigCategoryCaptcha, Name: ConfigCaptchaHeight, Label: "验证码高度", Value: "60", Placeholder: "请输入验证码高度,默认为60", InputType: InputTypeNumber, Sort: 13, Options: ""},
|
|
|
{Category: ConfigCategoryCaptcha, Name: ConfigCaptchaWidth, Label: "验证码宽度", Value: "240", Placeholder: "请输入验证码宽度,默认为240", InputType: InputTypeNumber, Sort: 14, Options: ""},
|
|
|
{Category: ConfigCategoryCaptcha, Name: ConfigCaptchaLength, Label: "验证码长度", Value: "5", Placeholder: "请输入验证码长度,默认为6", InputType: InputTypeNumber, Sort: 15, Options: ""},
|
|
|
{Category: ConfigCategoryCaptcha, Name: ConfigCaptchaType, Label: "验证码类型", Value: "digit", Placeholder: "请选择验证码类型,默认为数字", InputType: InputTypeSelect, Sort: 16, Options: captcha.CaptchaTypeOptions},
|
|
|
|
|
|
// 安全配置项
|
|
|
{Category: ConfigCategorySecurity, Name: ConfigSecurityMaxDocumentSize, Label: "最大文档大小(MB)", Value: "50", Placeholder: "允许用户上传的最大文档大小,默认为50,即50MB。配置时仍需配置反向代理服务所允许传输的最大大小,如nginx的client_max_body_size值!", InputType: InputTypeNumber, Sort: 1, Options: ""},
|
|
|
{Category: ConfigCategorySecurity, Name: ConfigSecurityCommentInterval, Label: "评论时间间隔", Value: "10", Placeholder: "用户评论时间间隔,单位为秒。0表示不限制。", InputType: InputTypeNumber, Sort: 2, Options: ""},
|
|
|
{Category: ConfigCategorySecurity, Name: ConfigSecurityDocumentRelatedDuration, Label: "文档的【相关文档】有效期", Value: "7", Placeholder: "文档的相关联文档的有效期,默认为7,即7天,0或小于0,表示不开启相关文档功能", InputType: InputTypeNumber, Sort: 15, Options: ""},
|
|
|
{Category: ConfigCategorySecurity, Name: ConfigSecurityAttachmentRetentionMinute, Label: "从回收站清除的文档文件保留时长", Value: "1440", Placeholder: "单位为分钟,默认为1440,即24小时。文档被从回收站清除之后,文档文件并未真正删除,用以规避操作失误的情况。设为 0 则表示即时删除文档文件,用以释放存储空间。", InputType: InputTypeNumber, Sort: 16, Options: ""},
|
|
|
{Category: ConfigCategorySecurity, Name: ConfigSecurityDocumentAllowedExt, Label: "允许上传的文档类型", Value: "", Placeholder: "留空表示允许程序所支持的全部文档类型", InputType: InputTypeSelectMulti, Sort: 30, Options: strings.Join(filetil.GetDocumentExts(), "\n")},
|
|
|
{Category: ConfigCategorySecurity, Name: ConfigSecurityIsClose, Label: "是否关闭网站", Value: "false", Placeholder: "请选择是否关闭网站", InputType: InputTypeSwitch, Sort: 160, Options: ""},
|
|
|
{Category: ConfigCategorySecurity, Name: ConfigSecurityCloseStatement, Label: "闭站说明", Value: closeStatement, Placeholder: "关闭网站后,页面提示内容(支持HTML)", InputType: InputTypeTextarea, Sort: 170, Options: ""},
|
|
|
{Category: ConfigCategorySecurity, Name: ConfigSecurityEnableRegister, Label: "是否允许注册", Value: "true", Placeholder: "请选择是否允许用户注册", InputType: InputTypeSwitch, Sort: 18, Options: ""},
|
|
|
{Category: ConfigCategorySecurity, Name: ConfigSecurityLoginRequired, Label: "是否登录才能访问", Value: "false", Placeholder: "用户是否登录了才能访问", InputType: InputTypeSwitch, Sort: 18, Options: ""},
|
|
|
{Category: ConfigCategorySecurity, Name: ConfigSecurityEnableCaptchaLogin, Label: "是否开启登录验证码", Value: "true", Placeholder: "请选择是否开启登录验证码", InputType: InputTypeSwitch, Sort: 19, Options: ""},
|
|
|
{Category: ConfigCategorySecurity, Name: ConfigSecurityEnableCaptchaRegister, Label: "是否开启注册验证码", Value: "true", Placeholder: "请选择是否开启注册验证码", InputType: InputTypeSwitch, Sort: 20, Options: ""},
|
|
|
{Category: ConfigCategorySecurity, Name: ConfigSecurityEnableCaptchaComment, Label: "是否开启评论验证码", Value: "true", Placeholder: "请选择是否开启评论验证码", InputType: InputTypeSwitch, Sort: 21, Options: ""},
|
|
|
{Category: ConfigCategorySecurity, Name: ConfigSecurityEnableCaptchaFindPassword, Label: "是否开启找回密码验证码", Value: "true", Placeholder: "请选择是否开启找回密码验证码", InputType: InputTypeSwitch, Sort: 22, Options: ""},
|
|
|
{Category: ConfigCategorySecurity, Name: ConfigSecurityEnableVerifyRegisterEmail, Label: "是否开启注册邮箱验证", Value: "false", Placeholder: "用户注册时是否需要验证邮箱", InputType: InputTypeSwitch, Sort: 50, Options: ""},
|
|
|
|
|
|
// 底部链接
|
|
|
{Category: ConfigCategoryFooter, Name: ConfigFooterAbout, Label: "关于我们", Value: "/article/about", Placeholder: "请输入关于我们的链接地址,留空表示不显示", InputType: InputTypeText, Sort: 24, Options: ""},
|
|
|
{Category: ConfigCategoryFooter, Name: ConfigFooterContact, Label: "联系我们", Value: "/article/contact", Placeholder: "请输入联系我们的链接地址,留空表示不显示", InputType: InputTypeText, Sort: 25, Options: ""},
|
|
|
{Category: ConfigCategoryFooter, Name: ConfigFooterAgreement, Label: "文库协议", Value: "/article/agreement", Placeholder: "请输入文库协议的链接地址,留空表示不显示", InputType: InputTypeText, Sort: 26, Options: ""},
|
|
|
{Category: ConfigCategoryFooter, Name: ConfigFooterCopyright, Label: "免责声明", Value: "/article/copyright", Placeholder: "请输入免责声明的链接地址,留空表示不显示", InputType: InputTypeText, Sort: 27, Options: ""},
|
|
|
{Category: ConfigCategoryFooter, Name: ConfigFooterFeedback, Label: "意见反馈", Value: "/article/feedback", Placeholder: "请输入意见反馈的链接地址,留空表示不显示", InputType: InputTypeText, Sort: 28, Options: ""},
|
|
|
|
|
|
// 转换配置项
|
|
|
{Category: ConfigCategoryConverter, Name: ConfigConverterMaxPreview, Label: "最大预览页数", Value: "12", Placeholder: "文档允许的最大预览页数,0表示不限制", InputType: InputTypeNumber, Sort: 0, Options: ""},
|
|
|
{Category: ConfigCategoryConverter, Name: ConfigConverterTimeout, Label: "转换超时(分钟)", Value: "30", Placeholder: "文档转换超时时间,默认为30分钟", InputType: InputTypeNumber, Sort: 10, Options: ""},
|
|
|
{Category: ConfigCategoryConverter, Name: ConfigConverterExtension, Label: "预览格式", Value: "svg", Placeholder: "将文档转为特定格式以供预览", InputType: InputTypeSelect, Sort: 20, Options: "svg:SVG\njpg:JPEG\npng:PNG\nwebp:WEBP"},
|
|
|
{Category: ConfigCategoryConverter, Name: ConfigConverterEnableGZIP, Label: "是否启用GZIP压缩", Value: "true", Placeholder: "是否对文档SVG预览文件启用GZIP压缩,启用后转换效率会【稍微】下降,但相对直接的SVG文件减少75%的存储空间", InputType: InputTypeSwitch, Sort: 30, Options: ""},
|
|
|
{Category: ConfigCategoryConverter, Name: ConfigConverterEnableSVGO, Label: "是否启用SVGO", Value: "false", Placeholder: "是否对文档SVG预览文件启用SVGO压缩,启用后转换效率会【明显】下降,但相对直接的SVG文件减少50%左右的存储空间", InputType: InputTypeSwitch, Sort: 40, Options: ""},
|
|
|
{Category: ConfigCategoryConverter, Name: ConfigConverterEnableConvertRepeatedDocument, Label: "是否转换重复文档", Value: "false", Placeholder: "对于已转换过的文档,再次被上传时是否再转换一次", InputType: InputTypeSwitch, Sort: 50, Options: ""},
|
|
|
|
|
|
// 下载配置
|
|
|
{Category: ConfigCategoryDownload, Name: ConfigDownloadEnableGuestDownload, Label: "是否允许游客下载", Value: "false", Placeholder: "启用之后,未登录用户可以下载免费文档", InputType: InputTypeSwitch, Sort: 10, Options: ""},
|
|
|
{Category: ConfigCategoryDownload, Name: ConfigDownloadFreeDownloadDuration, Label: "购买文档后多少天内允许免费重复下载", Value: "0", Placeholder: "0表示再次下载仍需购买,大于0表示指定多少天内有效", InputType: InputTypeNumber, Sort: 20, Options: ""},
|
|
|
{Category: ConfigCategoryDownload, Name: ConfigDownloadUrlDuration, Label: "下载链接有效时长(秒)", Value: "60", Placeholder: "生成文档下载链接后多少秒之后链接失效", InputType: InputTypeNumber, Sort: 30, Options: ""},
|
|
|
{Category: ConfigCategoryDownload, Name: ConfigDownloadTimesEveryDay, Label: "允许登录用户每天下载次数", Value: "10", Placeholder: "允许登录用户每天下载次数,0表示不允许下载", InputType: InputTypeNumber, Sort: 40, Options: ""},
|
|
|
{Category: ConfigCategoryDownload, Name: ConfigDownloadTimesEveryIP, Label: "允许每个IP每天下载次数", Value: "10", Placeholder: "允许每个IP每天下载的次数,0表示不允许下载(针对所有用户)", InputType: InputTypeNumber, Sort: 41, Options: ""},
|
|
|
{Category: ConfigCategoryDownload, Name: ConfigDownloadSecretKey, Label: "链接签名密钥", Value: "moredoc", Placeholder: "链接签名密钥,用于加密下载链接", InputType: InputTypeText, Sort: 50, Options: ""},
|
|
|
|
|
|
// 积分规则配置
|
|
|
{Category: ConfigCategoryScore, Name: ConfigScoreCreditName, Label: "积分名称", Value: "魔豆", Placeholder: "请输入网站的积分名称,默认为魔豆", InputType: InputTypeText, Sort: 1, Options: ""},
|
|
|
{Category: ConfigCategoryScore, Name: ConfigScoreRegister, Label: "注册", Value: "10", Placeholder: "注册时获得的积分", InputType: InputTypeNumber, Sort: 10, Options: ""},
|
|
|
{Category: ConfigCategoryScore, Name: ConfigScoreSignIn, Label: "签到", Value: "1", Placeholder: "每日签到获得的积分", InputType: InputTypeNumber, Sort: 20, Options: ""},
|
|
|
{Category: ConfigCategoryScore, Name: ConfigScoreDeleteDocument, Label: "删除文档", Value: "1", Placeholder: "删除上传文档扣除的积分,0表示不扣除", InputType: InputTypeNumber, Sort: 25, Options: ""},
|
|
|
{Category: ConfigCategoryScore, Name: ConfigScoreUploadDocument, Label: "上传文档", Value: "5", Placeholder: "上传一篇文档可获得的积分", InputType: InputTypeNumber, Sort: 30, Options: ""},
|
|
|
{Category: ConfigCategoryScore, Name: ConfigScoreUploadDocumentLimit, Label: "每日上传文档奖励次数", Value: "1", Placeholder: "每天最多可以获得多少次文档上传奖励,0表示无奖励。", InputType: InputTypeNumber, Sort: 40, Options: ""},
|
|
|
{Category: ConfigCategoryScore, Name: ConfigScoreDocumentCollected, Label: "文档被收藏", Value: "1", Placeholder: "上传的文档被收藏后获得的积分", InputType: InputTypeNumber, Sort: 50, Options: ""},
|
|
|
{Category: ConfigCategoryScore, Name: ConfigScoreDocumentCollectedLimit, Label: "每日文档被收藏奖励次数", Value: "1", Placeholder: "每天最多可以获得多少次文档被收藏奖励,0表示无奖励。", InputType: InputTypeNumber, Sort: 60, Options: ""},
|
|
|
{Category: ConfigCategoryScore, Name: ConfigScoreDocumentCommented, Label: "文档被评论", Value: "1", Placeholder: "上传的文档被评论后获得的积分", InputType: InputTypeNumber, Sort: 70, Options: ""},
|
|
|
{Category: ConfigCategoryScore, Name: ConfigScoreDocumentCommentedLimit, Label: "每日文档被评论奖励次数", Value: "1", Placeholder: "每天最多可以获得多少次文档被评论奖励,0表示无奖励。", InputType: InputTypeNumber, Sort: 80, Options: ""},
|
|
|
|
|
|
// 邮件配置
|
|
|
{Category: ConfigCategoryEmail, Name: ConfigEmailEnable, Label: "是否启用邮件服务", Value: "false", Placeholder: "邮件服务,用于找回账户密码", InputType: InputTypeSwitch, Sort: 10, Options: ""},
|
|
|
{Category: ConfigCategoryEmail, Name: ConfigEmailHost, Label: "SMTP 服务器地址", Value: "", Placeholder: "如:smtp.exmail.com", InputType: InputTypeText, Sort: 20, Options: ""},
|
|
|
{Category: ConfigCategoryEmail, Name: ConfigEmailPort, Label: "SMTP 服务器端口", Value: "465", Placeholder: "如:465", InputType: InputTypeNumber, Sort: 30, Options: ""},
|
|
|
{Category: ConfigCategoryEmail, Name: ConfigEmailIsTLS, Label: "是否启用TLS", Value: "true", Placeholder: "如果是TLS端口,请启用", InputType: InputTypeSwitch, Sort: 40, Options: ""},
|
|
|
{Category: ConfigCategoryEmail, Name: ConfigEmailFromName, Label: "发件人名称", Value: "", Placeholder: "请输入您要展示的发件人名称", InputType: InputTypeText, Sort: 50, Options: ""},
|
|
|
{Category: ConfigCategoryEmail, Name: ConfigEmailUsername, Label: "SMTP 账号", Value: "", Placeholder: "请输入您的邮箱账户", InputType: InputTypeText, Sort: 60, Options: ""},
|
|
|
{Category: ConfigCategoryEmail, Name: ConfigEmailPassword, Label: "SMTP 密码", Value: "", Placeholder: "请输入您的邮箱密码", InputType: InputTypePassword, Sort: 70, Options: "", IsSecret: true},
|
|
|
{Category: ConfigCategoryEmail, Name: ConfigEmailDuration, Label: "邮件有效期", Value: "30", Placeholder: "找回密码时链接有效期,默认为30,表示30分钟", InputType: InputTypeNumber, Sort: 80, Options: ""},
|
|
|
{Category: ConfigCategoryEmail, Name: ConfigEmailSecret, Label: "签名密钥", Value: "moredoc", Placeholder: "找回密码链接签名密钥", InputType: InputTypeText, Sort: 80, Options: ""},
|
|
|
{Category: ConfigCategoryEmail, Name: ConfigEmailTestEmail, Label: "测试邮箱", Value: "", Placeholder: "用于每次变更配置时保存发送测试邮件", InputType: InputTypeText, Sort: 90, Options: ""},
|
|
|
|
|
|
// 展示配置
|
|
|
{Category: ConfigCategoryDisplay, Name: ConfigDisplayShowRegisterUserCount, Label: "是否显示注册用户数", Value: "true", Placeholder: "网站首页,是否显示注册用户数", InputType: InputTypeSwitch, Sort: 10, Options: ""},
|
|
|
{Category: ConfigCategoryDisplay, Name: ConfigDisplayVirtualRegisterCount, Label: "网站虚拟注册用户数", Value: "0", Placeholder: "网站首页显示的用户数=真实注册用户数+虚拟注册用户数,用以避免网站初期用户注册数过少的尴尬窘境", InputType: InputTypeNumber, Sort: 20, Options: ""},
|
|
|
{Category: ConfigCategoryDisplay, Name: ConfigDisplayShowDocumentDescriptions, Label: "是否显示文档摘要", Value: "true", Placeholder: "文档阅读页面,是否显示文档摘要等信息", InputType: InputTypeSwitch, Sort: 21, Options: ""},
|
|
|
{Category: ConfigCategoryDisplay, Name: ConfigDisplayShowIndexCategories, Label: "是否显示横栏分类", Value: "true", Placeholder: "网站首页中间横栏位置,是否显示分类", InputType: InputTypeSwitch, Sort: 30, Options: ""},
|
|
|
{Category: ConfigCategoryDisplay, Name: ConfigDisplayPagesPerRead, Label: "文档【继续阅读】的页数", Value: "5", Placeholder: "用户阅读文档,每次点击继续阅读按钮时阅读的页数,默认为5,表示5页", InputType: InputTypeNumber, Sort: 40, Options: ""},
|
|
|
{Category: ConfigCategoryDisplay, Name: ConfigDisplayMaxSearchPages, Label: "文档搜索结果最大页数", Value: "100", Placeholder: "搜索结果,默认最大展示100页,0表示不限制", InputType: InputTypeNumber, Sort: 50, Options: ""},
|
|
|
{Category: ConfigCategoryDisplay, Name: ConfigDisplayCopyrightStatement, Label: "版权声明", Value: "本站文档数据由用户上传,仅供学习交流,如侵犯您的权益,请联系我们进行删除。", Placeholder: "网站最底部版权声明,支持HTML", InputType: InputTypeTextarea, Sort: 60, Options: ""},
|
|
|
}
|
|
|
|
|
|
for _, cfg := range cfgs {
|
|
|
existConfig, _ := m.GetConfigByNameCategory(cfg.Name, cfg.Category, "id")
|
|
|
if existConfig.Id > 0 {
|
|
|
// 更新除了值之外的所有字段
|
|
|
cfg.Id = existConfig.Id
|
|
|
err = m.db.Omit("value").Updates(&cfg).Error
|
|
|
if err != nil {
|
|
|
m.logger.Error("initConfig", zap.Error(err))
|
|
|
return
|
|
|
}
|
|
|
continue
|
|
|
}
|
|
|
err = m.CreateConfig(&cfg)
|
|
|
if err != nil {
|
|
|
m.logger.Error("initConfig", zap.Error(err))
|
|
|
return
|
|
|
}
|
|
|
}
|
|
|
return
|
|
|
}
|