mirror of
https://github.com/mindoc-org/mindoc.git
synced 2025-04-05 20:17:53 +08:00
实现找回密码功能
This commit is contained in:
parent
ff7dfadcfe
commit
5c535f5bff
1
.gitignore
vendored
1
.gitignore
vendored
@ -23,3 +23,4 @@ _testmain.go
|
||||
*.test
|
||||
*.prof
|
||||
.idea
|
||||
/conf/app.conf
|
||||
|
@ -26,7 +26,7 @@ func RegisterDataBase() {
|
||||
|
||||
port := beego.AppConfig.String("db_port")
|
||||
|
||||
dataSource := fmt.Sprintf("%s:%s@tcp(%s:%s)/%s?charset=utf8&parseTime=true&loc=%s",username,password,host,port,database,url.QueryEscape(timezone))
|
||||
dataSource := fmt.Sprintf("%s:%s@tcp(%s:%s)/%s?charset=utf8mb4&parseTime=true&loc=%s",username,password,host,port,database,url.QueryEscape(timezone))
|
||||
|
||||
|
||||
orm.RegisterDataBase("default", "mysql", dataSource)
|
||||
@ -47,22 +47,23 @@ func RegisterModel() {
|
||||
new(models.Attachment),
|
||||
new(models.Logger),
|
||||
new(models.CommentVote),
|
||||
new(models.MemberToken),
|
||||
)
|
||||
|
||||
}
|
||||
|
||||
func Initialization() {
|
||||
|
||||
o := orm.NewOrm()
|
||||
o.Raw("alter table "+models.NewMember().TableNameWithPrefix()+" convert to character set utf8mb4_general_ci;").Exec()
|
||||
o.Raw("alter table "+models.NewBook().TableNameWithPrefix()+" convert to character set utf8mb4_general_ci;").Exec()
|
||||
o.Raw("alter table "+models.NewRelationship().TableNameWithPrefix()+" convert to character set utf8mb4_general_ci;").Exec()
|
||||
o.Raw("alter table "+models.NewComment().TableNameWithPrefix()+" convert to character set utf8mb4_general_ci;").Exec()
|
||||
o.Raw("alter table " +models.NewOption().TableNameWithPrefix()+" convert to character set utf8mb4_general_ci;").Exec()
|
||||
o.Raw("alter table "+models.NewDocument().TableNameWithPrefix()+" convert to character set utf8mb4_general_ci;").Exec()
|
||||
o.Raw("alter table "+models.NewAttachment().TableNameWithPrefix()+" convert to character set utf8mb4_general_ci;").Exec()
|
||||
o.Raw("alter table "+models.NewLogger().TableNameWithPrefix()+" convert to character set utf8mb4_general_ci;").Exec()
|
||||
o.Raw("alter table "+models.NewCommentVote().TableNameWithPrefix()+" convert to character set utf8mb4_general_ci;").Exec()
|
||||
//o := orm.NewOrm()
|
||||
//o.Raw("alter table "+models.NewMember().TableNameWithPrefix()+" convert to character set utf8mb4_general_ci;").Exec()
|
||||
//o.Raw("alter table "+models.NewBook().TableNameWithPrefix()+" convert to character set utf8mb4_general_ci;").Exec()
|
||||
//o.Raw("alter table "+models.NewRelationship().TableNameWithPrefix()+" convert to character set utf8mb4_general_ci;").Exec()
|
||||
//o.Raw("alter table "+models.NewComment().TableNameWithPrefix()+" convert to character set utf8mb4_general_ci;").Exec()
|
||||
//o.Raw("alter table " +models.NewOption().TableNameWithPrefix()+" convert to character set utf8mb4_general_ci;").Exec()
|
||||
//o.Raw("alter table "+models.NewDocument().TableNameWithPrefix()+" convert to character set utf8mb4_general_ci;").Exec()
|
||||
//o.Raw("alter table "+models.NewAttachment().TableNameWithPrefix()+" convert to character set utf8mb4_general_ci;").Exec()
|
||||
//o.Raw("alter table "+models.NewLogger().TableNameWithPrefix()+" convert to character set utf8mb4_general_ci;").Exec()
|
||||
//o.Raw("alter table "+models.NewCommentVote().TableNameWithPrefix()+" convert to character set utf8mb4_general_ci;").Exec()
|
||||
|
||||
options := []models.Option {
|
||||
{ OptionName: "ENABLED_CAPTCHA", OptionValue: "false", OptionTitle:"是否启用验证码"},
|
||||
@ -91,7 +92,6 @@ func RegisterLogger() {
|
||||
logs.EnableFuncCallDepth(true)
|
||||
logs.Async()
|
||||
|
||||
//beego.BeeLogger.DelLogger("console")
|
||||
if _,err := os.Stat("logs/log.log"); os.IsNotExist(err) {
|
||||
if f,err := os.Create("logs/log.log");err == nil {
|
||||
f.Close()
|
||||
|
@ -1,31 +0,0 @@
|
||||
appname = godoc
|
||||
httpport = 8181
|
||||
runmode = dev
|
||||
sessionon = true
|
||||
sessionname = mindoc_id
|
||||
copyrequestbody = true
|
||||
|
||||
#默认Session生成Key的秘钥
|
||||
beegoserversessionkey=123456
|
||||
#Session储存方式
|
||||
sessionprovider=file
|
||||
sessionproviderconfig=./logs
|
||||
|
||||
#时区设置
|
||||
timezone = Asia/Shanghai
|
||||
|
||||
#数据库配置
|
||||
db_host=127.0.0.1
|
||||
db_port=3306
|
||||
db_database=mindoc_db
|
||||
db_username=root
|
||||
db_password=123456
|
||||
|
||||
#项目默认封面
|
||||
cover=/static/images/book.jpg
|
||||
|
||||
#默认编辑器
|
||||
editor=markdown
|
||||
|
||||
#上传文件的后缀
|
||||
upload_file_ext=txt|doc|docx|xls|xlsx|ppt|pptx|pdf|7z|rar|jpg|jpeg|png|gif
|
@ -34,4 +34,22 @@ avatar=/static/images/headimgurl.jpg
|
||||
token_size=12
|
||||
|
||||
#上传文件的后缀
|
||||
upload_file_ext=txt|doc|docx|xls|xlsx|ppt|pptx|pdf|7z|rar|jpg|jpeg|png|gif
|
||||
upload_file_ext=txt|doc|docx|xls|xlsx|ppt|pptx|pdf|7z|rar|jpg|jpeg|png|gif
|
||||
|
||||
####################邮件配置######################
|
||||
#是否启用邮件
|
||||
enable_mail=false
|
||||
#每小时限制指定邮箱邮件发送次数
|
||||
mail_number=5
|
||||
#smtp服务用户名
|
||||
smtp_user_name=admin@iminho.me
|
||||
#smtp服务器地址
|
||||
smtp_host=smtp.ym.163.com
|
||||
#smtp密码
|
||||
smtp_password=
|
||||
#端口号
|
||||
smtp_port=25
|
||||
#发送邮件的显示名称
|
||||
form_user_name=admin@iminho.me
|
||||
#邮件有效期30分钟
|
||||
mail_expired=30
|
@ -52,14 +52,17 @@ func GetDefaultAvatar() string {
|
||||
return beego.AppConfig.DefaultString("avatar","/static/images/headimgurl.jpg")
|
||||
}
|
||||
|
||||
//获取阅读令牌长度.
|
||||
func GetTokenSize() int {
|
||||
return beego.AppConfig.DefaultInt("token_size",12)
|
||||
}
|
||||
|
||||
//获取默认文档封面.
|
||||
func GetDefaultCover() string {
|
||||
return beego.AppConfig.DefaultString("cover","/static/images/book.jpg")
|
||||
}
|
||||
|
||||
//获取允许的商城文件的类型.
|
||||
func GetUploadFileExt() []string {
|
||||
ext := beego.AppConfig.DefaultString("upload_file_ext","png|jpg|jpeg|gif|txt|doc|docx|pdf")
|
||||
|
||||
@ -76,7 +79,7 @@ func GetUploadFileExt() []string {
|
||||
}
|
||||
return exts
|
||||
}
|
||||
|
||||
//判断是否是允许商城的文件类型.
|
||||
func IsAllowUploadFileExt(ext string) bool {
|
||||
|
||||
if strings.HasPrefix(ext,".") {
|
||||
@ -90,4 +93,9 @@ func IsAllowUploadFileExt(ext string) bool {
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
//获取当前版本.
|
||||
func Version() string {
|
||||
return "v0.1"
|
||||
}
|
38
conf/mail.go
Normal file
38
conf/mail.go
Normal file
@ -0,0 +1,38 @@
|
||||
package conf
|
||||
|
||||
import (
|
||||
"github.com/astaxie/beego"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type SmtpConf struct {
|
||||
EnableMail bool
|
||||
MailNumber int
|
||||
SmtpUserName string
|
||||
SmtpHost string
|
||||
SmtpPassword string
|
||||
SmtpPort int
|
||||
FormUserName string
|
||||
MailExpired int
|
||||
}
|
||||
|
||||
func GetMailConfig() *SmtpConf {
|
||||
user_name := beego.AppConfig.String("smtp_user_name")
|
||||
password := beego.AppConfig.String("smtp_password")
|
||||
smtp_host := beego.AppConfig.String("smtp_host")
|
||||
smtp_port := beego.AppConfig.DefaultInt("smtp_port",25)
|
||||
form_user_name := beego.AppConfig.String("form_user_name")
|
||||
enable_mail := beego.AppConfig.String("enable_mail")
|
||||
mail_number := beego.AppConfig.DefaultInt("mail_number",5)
|
||||
|
||||
c := &SmtpConf{
|
||||
EnableMail : strings.EqualFold(enable_mail,"true"),
|
||||
MailNumber: mail_number,
|
||||
SmtpUserName:user_name,
|
||||
SmtpHost:smtp_host,
|
||||
SmtpPassword:password,
|
||||
FormUserName:form_user_name,
|
||||
SmtpPort:smtp_port,
|
||||
}
|
||||
return c
|
||||
}
|
@ -3,15 +3,16 @@ package controllers
|
||||
import (
|
||||
"time"
|
||||
"strings"
|
||||
"regexp"
|
||||
|
||||
"net/smtp"
|
||||
"github.com/lifei6671/godoc/conf"
|
||||
"github.com/lifei6671/godoc/models"
|
||||
"github.com/lifei6671/godoc/utils"
|
||||
"github.com/astaxie/beego"
|
||||
"github.com/astaxie/beego/logs"
|
||||
"github.com/lifei6671/gocaptcha"
|
||||
|
||||
"regexp"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
// AccountController 用户登录与注册.
|
||||
@ -30,10 +31,7 @@ func (c *AccountController) Login() {
|
||||
if cookie,ok := c.GetSecureCookie(conf.GetAppKey(),"login");ok{
|
||||
|
||||
if err := utils.Decode(cookie,&remember); err == nil {
|
||||
member := models.NewMember()
|
||||
member.MemberId = remember.MemberId
|
||||
|
||||
if err := models.NewMember().Find(remember.MemberId); err == nil {
|
||||
if member,err := models.NewMember().Find(remember.MemberId); err == nil {
|
||||
c.SetMember(*member)
|
||||
|
||||
c.Redirect(beego.URLFor("HomeController.Index"), 302)
|
||||
@ -137,9 +135,193 @@ func (c *AccountController) Register() {
|
||||
}
|
||||
}
|
||||
|
||||
func (p *AccountController) FindPassword() {
|
||||
p.TplName = "account/find_password.tpl"
|
||||
//找回密码.
|
||||
func (c *AccountController) FindPassword() {
|
||||
c.TplName = "account/find_password_setp1.tpl"
|
||||
mail_conf := conf.GetMailConfig()
|
||||
|
||||
if c.Ctx.Input.IsPost() {
|
||||
|
||||
email := c.GetString("email")
|
||||
captcha := c.GetString("code")
|
||||
|
||||
if email == "" {
|
||||
c.JsonResult(6005,"邮箱地址不能为空")
|
||||
}
|
||||
if !mail_conf.EnableMail {
|
||||
c.JsonResult(6004,"未启用邮件服务")
|
||||
}
|
||||
|
||||
//如果开启了验证码
|
||||
if v,ok := c.Option["ENABLED_CAPTCHA"]; ok && strings.EqualFold(v,"true") {
|
||||
v,ok := c.GetSession(conf.CaptchaSessionName).(string);
|
||||
if !ok || !strings.EqualFold(v,captcha){
|
||||
c.JsonResult(6001,"验证码不正确")
|
||||
}
|
||||
}
|
||||
|
||||
member ,err := models.NewMember().FindByFieldFirst("email",email)
|
||||
if err != nil {
|
||||
c.JsonResult(6006,"邮箱不存在")
|
||||
}
|
||||
if member.Status != 0 {
|
||||
c.JsonResult(6007,"账号已被禁用")
|
||||
}
|
||||
|
||||
|
||||
count,err := models.NewMemberToken().FindSendCount(email,time.Now().Add(-1*time.Hour),time.Now())
|
||||
|
||||
if err != nil {
|
||||
beego.Error(err)
|
||||
c.JsonResult(6008,"发送邮件失败")
|
||||
}
|
||||
if count > mail_conf.MailNumber {
|
||||
c.JsonResult(6008,"发送次数太多,请稍候再试")
|
||||
}
|
||||
|
||||
member_token := models.NewMemberToken()
|
||||
|
||||
member_token.Token = string(utils.Krand(32,utils.KC_RAND_KIND_ALL))
|
||||
member_token.Email = email
|
||||
member_token.MemberId = member.MemberId
|
||||
member_token.IsValid = false
|
||||
if _,err := member_token.InsertOrUpdate(); err != nil {
|
||||
c.JsonResult(6009,"邮件发送失败")
|
||||
}
|
||||
|
||||
data := map[string]interface{}{
|
||||
"SITE_NAME" : c.Option["SITE_NAME"],
|
||||
"url" : c.BaseUrl() + beego.URLFor("AccountController.FindPassword", "token",member_token.Token,"mail",email),
|
||||
}
|
||||
|
||||
body,err := c.ExecuteViewPathTemplate("account/mail_template.tpl",data)
|
||||
if err != nil {
|
||||
beego.Error(err)
|
||||
c.JsonResult(6003,"邮件发送失败")
|
||||
}
|
||||
|
||||
go func(mail_conf *conf.SmtpConf,email string,body string) {
|
||||
auth := smtp.PlainAuth(
|
||||
"",
|
||||
mail_conf.SmtpUserName,
|
||||
mail_conf.SmtpPassword,
|
||||
mail_conf.SmtpHost,
|
||||
)
|
||||
|
||||
mime := "MIME-version: 1.0;\nContent-Type: text/html; charset=\"UTF-8\";\n\n";
|
||||
subject := "Subject: 找回密码!\n"
|
||||
|
||||
err = smtp.SendMail(
|
||||
mail_conf.SmtpHost + ":" + strconv.Itoa(mail_conf.SmtpPort),
|
||||
auth,
|
||||
mail_conf.FormUserName,
|
||||
[]string{ email },
|
||||
[]byte(subject + mime +"\n" +body),
|
||||
)
|
||||
if err != nil {
|
||||
beego.Error("邮件发送失败 => ",email,err)
|
||||
}
|
||||
}(mail_conf,email,body)
|
||||
|
||||
|
||||
c.JsonResult(0,"ok", c.BaseUrl() + beego.URLFor("AccountController.Login"))
|
||||
}
|
||||
|
||||
token := c.GetString("token")
|
||||
mail := c.GetString("mail")
|
||||
|
||||
if token != "" && mail != "" {
|
||||
member_token,err := models.NewMemberToken().FindByFieldFirst("token",token)
|
||||
|
||||
if err != nil {
|
||||
beego.Error(err)
|
||||
c.Data["ErrorMessage"] = "邮件已失效"
|
||||
c.TplName = "errors/error.tpl"
|
||||
return
|
||||
}
|
||||
sub_time := member_token.SendTime.Sub(time.Now())
|
||||
|
||||
if !strings.EqualFold(member_token.Email,mail) || sub_time.Minutes() > float64(mail_conf.MailExpired) || !member_token.ValidTime.IsZero() {
|
||||
c.Data["ErrorMessage"] = "验证码已过期,请重新操作。"
|
||||
c.TplName = "errors/error.tpl"
|
||||
return
|
||||
}
|
||||
c.Data["Email"] = member_token.Email
|
||||
c.Data["Token"] = member_token.Token
|
||||
c.TplName = "account/find_password_setp2.tpl"
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
//校验邮件并修改密码.
|
||||
func (c *AccountController) ValidEmail() {
|
||||
c.Prepare()
|
||||
password1 := c.GetString("password1")
|
||||
password2 := c.GetString("password2")
|
||||
captcha := c.GetString("code")
|
||||
token := c.GetString("token")
|
||||
mail := c.GetString("mail")
|
||||
|
||||
if password1 == "" {
|
||||
c.JsonResult(6001,"密码不能为空")
|
||||
}
|
||||
if l := strings.Count(password1,""); l <6 || l > 50{
|
||||
c.JsonResult(6001,"密码不能为空且必须在6-50个字符之间")
|
||||
}
|
||||
if password2 == ""{
|
||||
c.JsonResult(6002,"确认密码不能为空")
|
||||
}
|
||||
if password1 != password2 {
|
||||
c.JsonResult(6003,"确认密码输入不正确")
|
||||
}
|
||||
if captcha == "" {
|
||||
c.JsonResult(6004,"验证码不能为空")
|
||||
}
|
||||
v,ok := c.GetSession(conf.CaptchaSessionName).(string);
|
||||
if !ok || !strings.EqualFold(v,captcha){
|
||||
c.JsonResult(6001,"验证码不正确")
|
||||
}
|
||||
|
||||
mail_conf := conf.GetMailConfig()
|
||||
member_token,err := models.NewMemberToken().FindByFieldFirst("token",token)
|
||||
|
||||
if err != nil {
|
||||
beego.Error(err)
|
||||
c.JsonResult(6007,"邮件已失效")
|
||||
}
|
||||
sub_time := member_token.SendTime.Sub(time.Now())
|
||||
|
||||
if !strings.EqualFold(member_token.Email,mail) || sub_time.Minutes() > float64(mail_conf.MailExpired) || !member_token.ValidTime.IsZero() {
|
||||
|
||||
c.JsonResult(6008,"验证码已过期,请重新操作。")
|
||||
}
|
||||
member ,err := models.NewMember().Find(member_token.MemberId)
|
||||
if err != nil{
|
||||
beego.Error(err)
|
||||
c.JsonResult(6005,"用户不存在")
|
||||
}
|
||||
hash ,err := utils.PasswordHash(password1);
|
||||
|
||||
if err != nil {
|
||||
beego.Error(err)
|
||||
c.JsonResult(6006,"保存密码失败")
|
||||
}
|
||||
|
||||
member.Password = hash
|
||||
|
||||
err = member.Update("password")
|
||||
member_token.ValidTime = time.Now()
|
||||
member_token.IsValid = true
|
||||
member_token.InsertOrUpdate()
|
||||
|
||||
if err != nil {
|
||||
beego.Error(err)
|
||||
c.JsonResult(6006,"保存密码失败")
|
||||
}
|
||||
c.JsonResult(0,"ok",c.BaseUrl() + beego.URLFor("AccountController.Login"))
|
||||
}
|
||||
|
||||
|
||||
// Logout 退出登录.
|
||||
func (c *AccountController) Logout(){
|
||||
c.SetMember(models.Member{});
|
||||
|
@ -8,6 +8,8 @@ import (
|
||||
"github.com/lifei6671/godoc/conf"
|
||||
"github.com/astaxie/beego"
|
||||
"strings"
|
||||
"encoding/json"
|
||||
"io"
|
||||
)
|
||||
|
||||
|
||||
@ -62,17 +64,25 @@ func (c *BaseController) SetMember(member models.Member) {
|
||||
|
||||
// JsonResult 响应 json 结果
|
||||
func (c *BaseController) JsonResult(errCode int,errMsg string,data ...interface{}){
|
||||
json := make(map[string]interface{},3)
|
||||
jsonData := make(map[string]interface{},3)
|
||||
|
||||
json["errcode"] = errCode
|
||||
json["message"] = errMsg
|
||||
jsonData["errcode"] = errCode
|
||||
jsonData["message"] = errMsg
|
||||
|
||||
if len(data) > 0 && data[0] != nil{
|
||||
json["data"] = data[0]
|
||||
jsonData["data"] = data[0]
|
||||
}
|
||||
|
||||
c.Data["json"] = json
|
||||
c.ServeJSON(true)
|
||||
returnJSON, err := json.Marshal(jsonData)
|
||||
|
||||
if err != nil {
|
||||
beego.Error(err)
|
||||
}
|
||||
|
||||
c.Ctx.ResponseWriter.Header().Set("Content-Type", "application/json; charset=utf-8")
|
||||
|
||||
io.WriteString(c.Ctx.ResponseWriter,string(returnJSON))
|
||||
|
||||
c.StopRun()
|
||||
}
|
||||
|
||||
|
@ -88,7 +88,7 @@ func (c *BookMemberController) ChangeRole() {
|
||||
|
||||
member := models.NewMember()
|
||||
|
||||
if err := member.Find(member_id); err != nil {
|
||||
if _,err := member.Find(member_id); err != nil {
|
||||
c.JsonResult(6003,"用户不存在")
|
||||
}
|
||||
if member.Status == 1 {
|
||||
|
17
controllers/error.go
Normal file
17
controllers/error.go
Normal file
@ -0,0 +1,17 @@
|
||||
package controllers
|
||||
|
||||
type ErrorController struct {
|
||||
BaseController
|
||||
}
|
||||
|
||||
func (c *ErrorController) Error404() {
|
||||
c.TplName = "errors/404.tpl"
|
||||
}
|
||||
|
||||
func (c *ErrorController) Error403() {
|
||||
c.TplName = "errors/403.tpl"
|
||||
}
|
||||
|
||||
func (c *ErrorController) Error500() {
|
||||
c.TplName = "errors/error.tpl"
|
||||
}
|
@ -139,7 +139,7 @@ func (c *ManagerController) UpdateMemberStatus() {
|
||||
}
|
||||
member := models.NewMember()
|
||||
|
||||
if err := member.Find(member_id); err != nil {
|
||||
if _,err := member.Find(member_id); err != nil {
|
||||
c.JsonResult(6002,"用户不存在")
|
||||
}
|
||||
member.Status = status
|
||||
@ -168,7 +168,7 @@ func (c *ManagerController) ChangeMemberRole() {
|
||||
}
|
||||
member := models.NewMember()
|
||||
|
||||
if err := member.Find(member_id); err != nil {
|
||||
if _,err := member.Find(member_id); err != nil {
|
||||
c.JsonResult(6002,"用户不存在")
|
||||
}
|
||||
member.Role = role
|
||||
|
@ -141,8 +141,7 @@ func (c *SettingController) Upload() {
|
||||
|
||||
url := "/" + filePath
|
||||
|
||||
member := models.NewMember()
|
||||
if err := member.Find(c.Member.MemberId);err == nil {
|
||||
if member,err := models.NewMember().Find(c.Member.MemberId);err == nil {
|
||||
member.Avatar = url
|
||||
member.Update()
|
||||
c.SetMember(*member)
|
||||
|
3
main.go
3
main.go
@ -7,6 +7,7 @@ import (
|
||||
"github.com/lifei6671/godoc/commands"
|
||||
"fmt"
|
||||
"os"
|
||||
"github.com/lifei6671/godoc/controllers"
|
||||
)
|
||||
|
||||
func main() {
|
||||
@ -21,5 +22,7 @@ func main() {
|
||||
|
||||
fmt.Println(os.Args[0])
|
||||
|
||||
beego.ErrorController(&controllers.ErrorController{})
|
||||
beego.Run()
|
||||
}
|
||||
|
||||
|
@ -73,9 +73,7 @@ func (m *BookResult) FindByIdentify(identify string,member_id int) (*BookResult,
|
||||
return m, ErrPermissionDenied
|
||||
}
|
||||
|
||||
member := NewMember()
|
||||
|
||||
err = member.Find(relationship2.MemberId)
|
||||
member, err := NewMember().Find(relationship2.MemberId)
|
||||
if err != nil {
|
||||
return m, err
|
||||
}
|
||||
|
@ -118,7 +118,7 @@ func (m *Comment) Insert() error {
|
||||
if m.MemberId > 0 {
|
||||
member := NewMember()
|
||||
//如果用户不存在
|
||||
if err := member.Find(m.MemberId) ; err != nil {
|
||||
if _,err := member.Find(m.MemberId) ; err != nil {
|
||||
return ErrMemberNoExist
|
||||
}
|
||||
//如果用户被禁用
|
||||
|
@ -7,6 +7,9 @@ import (
|
||||
"github.com/lifei6671/godoc/utils"
|
||||
"github.com/lifei6671/godoc/conf"
|
||||
"github.com/astaxie/beego/logs"
|
||||
"errors"
|
||||
"regexp"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type Member struct {
|
||||
@ -14,7 +17,7 @@ type Member struct {
|
||||
Account string `orm:"size(100);unique;column(account)" json:"account"`
|
||||
Password string `orm:"size(1000);column(password)" json:"-"`
|
||||
Description string `orm:"column(description);size(2000)" json:"description"`
|
||||
Email string `orm:"size(255);column(email);null;default(null)" json:"email"`
|
||||
Email string `orm:"size(255);column(email);unique" json:"email"`
|
||||
Phone string `orm:"size(255);column(phone);null;default(null)" json:"phone"`
|
||||
Avatar string `orm:"size(1000);column(avatar)" json:"avatar"`
|
||||
//用户角色:0 超级管理员 /1 管理员/ 2 普通用户 .
|
||||
@ -71,6 +74,22 @@ func (m *Member) Login(account string,password string) (*Member,error) {
|
||||
func (m *Member) Add () (error) {
|
||||
o := orm.NewOrm()
|
||||
|
||||
if ok,err := regexp.MatchString(conf.RegexpAccount,m.Account); m.Account == "" || !ok || err != nil {
|
||||
return errors.New("账号只能由英文字母数字组成,且在3-50个字符")
|
||||
}
|
||||
if m.Email == "" {
|
||||
return errors.New("邮箱不能为空")
|
||||
}
|
||||
if ok,err := regexp.MatchString(conf.RegexpEmail,m.Email); !ok || err != nil || m.Email == "" {
|
||||
return errors.New("邮箱格式不正确")
|
||||
}
|
||||
if l := strings.Count(m.Password,""); l <6 || l > 50{
|
||||
return errors.New("密码不能为空且必须在6-50个字符之间")
|
||||
}
|
||||
if c,err := o.QueryTable(m.TableNameWithPrefix()).Filter("email",m.Email).Count(); err == nil || c > 0 {
|
||||
return errors.New("邮箱已被使用")
|
||||
}
|
||||
|
||||
hash ,err := utils.PasswordHash(m.Password);
|
||||
|
||||
if err != nil {
|
||||
@ -92,21 +111,24 @@ func (m *Member) Add () (error) {
|
||||
func (m *Member) Update(cols... string) (error) {
|
||||
o := orm.NewOrm()
|
||||
|
||||
if m.Email == "" {
|
||||
return errors.New("邮箱不能为空")
|
||||
}
|
||||
if _,err := o.Update(m,cols...);err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *Member) Find(id int) error{
|
||||
func (m *Member) Find(id int) (*Member,error){
|
||||
o := orm.NewOrm()
|
||||
|
||||
m.MemberId = id
|
||||
if err := o.Read(m); err != nil {
|
||||
return err
|
||||
return m,err
|
||||
}
|
||||
m.ResolveRoleName()
|
||||
return nil
|
||||
return m,nil
|
||||
}
|
||||
|
||||
func (m *Member) ResolveRoleName (){
|
||||
@ -163,7 +185,13 @@ func (c *Member) IsAdministrator() bool {
|
||||
return c.Role == 0 || c.Role == 1
|
||||
}
|
||||
|
||||
|
||||
func (m *Member) FindByFieldFirst(field string,value interface{}) (*Member,error) {
|
||||
o := orm.NewOrm()
|
||||
|
||||
err := o.QueryTable(m.TableNameWithPrefix()).Filter(field,value).OrderBy("-member_id").One(m)
|
||||
|
||||
return m,err
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
66
models/member_token.go
Normal file
66
models/member_token.go
Normal file
@ -0,0 +1,66 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
"time"
|
||||
"github.com/lifei6671/godoc/conf"
|
||||
"github.com/astaxie/beego/orm"
|
||||
)
|
||||
|
||||
type MemberToken struct {
|
||||
TokenId int `orm:"column(token_id);pk;auto;unique" json:"token_id"`
|
||||
MemberId int `orm:"column(member_id);type(int)" json:"member_id"`
|
||||
Token string `orm:"column(token);size(255);index" json:"token"`
|
||||
Email string `orm:"column(email);size(255)" json:"email"`
|
||||
IsValid bool `orm:"column(is_valid)" json:"is_valid"`
|
||||
ValidTime time.Time `orm:"column(valid_time);null" json:"valid_time"`
|
||||
SendTime time.Time `orm:"column(send_time);auto_now_add;type(datetime)" json:"send_time"`
|
||||
}
|
||||
|
||||
|
||||
// TableName 获取对应数据库表名.
|
||||
func (m *MemberToken) TableName() string {
|
||||
return "member_token"
|
||||
}
|
||||
// TableEngine 获取数据使用的引擎.
|
||||
func (m *MemberToken) TableEngine() string {
|
||||
return "INNODB"
|
||||
}
|
||||
|
||||
func (m *MemberToken)TableNameWithPrefix() string {
|
||||
return conf.GetDatabasePrefix() + m.TableName()
|
||||
}
|
||||
|
||||
func NewMemberToken() *MemberToken {
|
||||
return &MemberToken{}
|
||||
}
|
||||
|
||||
func (m *MemberToken) InsertOrUpdate() (*MemberToken,error){
|
||||
o := orm.NewOrm()
|
||||
|
||||
if m.TokenId > 0 {
|
||||
_,err := o.Update(m)
|
||||
return m,err
|
||||
}
|
||||
_,err := o.Insert(m)
|
||||
|
||||
return m,err
|
||||
}
|
||||
|
||||
func (m *MemberToken) FindByFieldFirst(field string,value interface{}) (*MemberToken,error) {
|
||||
o := orm.NewOrm()
|
||||
|
||||
err := o.QueryTable(m.TableNameWithPrefix()).Filter(field,value).OrderBy("-token_id").One(m)
|
||||
|
||||
return m,err
|
||||
}
|
||||
|
||||
func (m *MemberToken) FindSendCount(mail string,start_time time.Time,end_time time.Time) (int ,error) {
|
||||
o := orm.NewOrm()
|
||||
|
||||
c,err := o.QueryTable(m.TableNameWithPrefix()).Filter("send_time__gte",start_time.Format("2006-01-02 15:04:05")).Filter("send_time__lte",end_time.Format("2006-01-02 15:04:05")).Count()
|
||||
|
||||
if err != nil {
|
||||
return 0,err
|
||||
}
|
||||
return int(c),nil
|
||||
}
|
@ -22,4 +22,11 @@ func init() {
|
||||
beego.InsertFilter("/book",beego.BeforeRouter,FilterUser)
|
||||
beego.InsertFilter("/book/*",beego.BeforeRouter,FilterUser)
|
||||
beego.InsertFilter("/api/*",beego.BeforeRouter,FilterUser)
|
||||
|
||||
var FinishRouter = func(ctx *context.Context) {
|
||||
ctx.ResponseWriter.Header().Add("MinDoc-Version",conf.Version())
|
||||
ctx.ResponseWriter.Header().Add("MinDoc-Site","http://www.iminho.me")
|
||||
}
|
||||
|
||||
beego.InsertFilter("/*",beego.BeforeRouter ,FinishRouter, false)
|
||||
}
|
||||
|
@ -12,6 +12,7 @@ func init() {
|
||||
beego.Router("/logout", &controllers.AccountController{},"*:Logout")
|
||||
beego.Router("/register", &controllers.AccountController{},"*:Register")
|
||||
beego.Router("/find_password", &controllers.AccountController{},"*:FindPassword")
|
||||
beego.Router("/valid_email", &controllers.AccountController{},"post:ValidEmail")
|
||||
beego.Router("/captcha", &controllers.AccountController{},"*:Captcha")
|
||||
|
||||
beego.Router("/manager", &controllers.ManagerController{},"*:Index")
|
||||
|
Binary file not shown.
BIN
static/fonts/BigBlocko.ttf
Normal file
BIN
static/fonts/BigBlocko.ttf
Normal file
Binary file not shown.
BIN
static/fonts/Bitsumishi.ttf
Normal file
BIN
static/fonts/Bitsumishi.ttf
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
static/fonts/Pointy.ttf
Normal file
BIN
static/fonts/Pointy.ttf
Normal file
Binary file not shown.
Binary file not shown.
16
static/fonts/lato-100.css
Normal file
16
static/fonts/lato-100.css
Normal file
@ -0,0 +1,16 @@
|
||||
/* latin-ext */
|
||||
@font-face {
|
||||
font-family: 'Lato';
|
||||
font-style: normal;
|
||||
font-weight: 100;
|
||||
src: local('Lato Hairline'), local('Lato-Hairline'), url(lato/v11/eFRpvGLEW31oiexbYNx7Y_esZW2xOQ-xsNqO47m55DA.woff2) format('woff2');
|
||||
unicode-range: U+0100-024F, U+1E00-1EFF, U+20A0-20AB, U+20AD-20CF, U+2C60-2C7F, U+A720-A7FF;
|
||||
}
|
||||
/* latin */
|
||||
@font-face {
|
||||
font-family: 'Lato';
|
||||
font-style: normal;
|
||||
font-weight: 100;
|
||||
src: local('Lato Hairline'), local('Lato-Hairline'), url(lato/v11/GtRkRNTnri0g82CjKnEB0Q.woff2) format('woff2');
|
||||
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2212, U+2215;
|
||||
}
|
BIN
static/fonts/lato/v11/GtRkRNTnri0g82CjKnEB0Q.woff2
Normal file
BIN
static/fonts/lato/v11/GtRkRNTnri0g82CjKnEB0Q.woff2
Normal file
Binary file not shown.
Binary file not shown.
130
views/account/find_password_setp1.tpl
Normal file
130
views/account/find_password_setp1.tpl
Normal file
@ -0,0 +1,130 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-cn">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<link rel="shortcut icon" href="/favicon.ico">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
|
||||
<meta name="renderer" content="webkit" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta name="author" content="SmartWiki" />
|
||||
<title>找回密码 - Powered by MinDoc</title>
|
||||
|
||||
<!-- Bootstrap -->
|
||||
<link href="/static/bootstrap/css/bootstrap.min.css" rel="stylesheet">
|
||||
<link href="/static/font-awesome/css/font-awesome.min.css" rel="stylesheet">
|
||||
<link href="/static/css/main.css" rel="stylesheet">
|
||||
<!-- HTML5 shim and Respond.js for IE8 support of HTML5 elements and media queries -->
|
||||
<!-- WARNING: Respond.js doesn't work if you view the page via file:// -->
|
||||
<!--[if lt IE 9]>
|
||||
<script src="/static/html5shiv/3.7.3/html5shiv.min.js"></script>
|
||||
<script src="/static/respond.js/1.4.2/respond.min.js"></script>
|
||||
<![endif]-->
|
||||
<!-- jQuery (necessary for Bootstrap's JavaScript plugins) -->
|
||||
<script src="/static/jquery/1.12.4/jquery.min.js"></script>
|
||||
</head>
|
||||
<body class="manual-container">
|
||||
<header class="navbar navbar-static-top smart-nav navbar-fixed-top manual-header" role="banner">
|
||||
<div class="container">
|
||||
<div class="navbar-header col-sm-12 col-md-6 col-lg-5">
|
||||
<a href="/" class="navbar-brand">MinDoc</a>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
<div class="container manual-body">
|
||||
<div class="row login">
|
||||
<div class="login-body">
|
||||
<form role="form" method="post" id="findPasswordForm">
|
||||
<h3 class="text-center">找回密码</h3>
|
||||
<div class="form-group">
|
||||
<div class="input-group">
|
||||
<div class="input-group-addon">
|
||||
<i class="fa fa-at"></i>
|
||||
</div>
|
||||
<input type="text" class="form-control" placeholder="邮箱" name="email" id="email" autocomplete="off">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="input-group" style="float: left;width: 195px;">
|
||||
<div class="input-group-addon">
|
||||
<i class="fa fa-check-square"></i>
|
||||
</div>
|
||||
<input type="text" name="code" id="code" class="form-control" style="width: 150px" maxlength="5" placeholder="验证码" autocomplete="off">
|
||||
</div>
|
||||
<img id="captcha-img" style="width: 140px;height: 40px;display: inline-block;float: right" src="{{urlfor "AccountController.Captcha"}}" onclick="this.src='{{urlfor "AccountController.Captcha"}}?key=login&t='+(new Date()).getTime();" title="点击换一张">
|
||||
<div class="clearfix"></div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<button type="submit" id="btnSendMail" class="btn btn-success" style="width: 100%" data-loading-text="正在处理..." autocomplete="off">找回密码</button>
|
||||
</div>
|
||||
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<div class="clearfix"></div>
|
||||
</div>
|
||||
{{template "widgets/footer.tpl" .}}
|
||||
<!-- Include all compiled plugins (below), or include individual files as needed -->
|
||||
<script src="/static/bootstrap/js/bootstrap.min.js" type="text/javascript"></script>
|
||||
<script src="/static/layer/layer.js" type="text/javascript"></script>
|
||||
<script src="/static/js/jquery.form.js" type="text/javascript"></script>
|
||||
<script type="text/javascript">
|
||||
$(function () {
|
||||
$("#email,#code").on('focus',function () {
|
||||
$(this).tooltip('destroy').parents('.form-group').removeClass('has-error');;
|
||||
});
|
||||
|
||||
$(document).keydown(function (e) {
|
||||
var event = document.all ? window.event : e;
|
||||
if(event.keyCode == 13){
|
||||
$("#btn-login").click();
|
||||
}
|
||||
});
|
||||
|
||||
$("#findPasswordForm").ajaxForm({
|
||||
beforeSubmit : function () {
|
||||
var $btn = $(this).button('loading');
|
||||
|
||||
var email = $.trim($("#email").val());
|
||||
if(email === ""){
|
||||
$("#email").tooltip({placement:"auto",title : "邮箱不能为空",trigger : 'manual'})
|
||||
.tooltip('show')
|
||||
.parents('.form-group').addClass('has-error');
|
||||
$btn.button('reset');
|
||||
return false;
|
||||
|
||||
}
|
||||
var code = $.trim($("#code").val());
|
||||
if(code === ""){
|
||||
$("#code").tooltip({title : '验证码不能为空',trigger : 'manual'})
|
||||
.tooltip('show')
|
||||
.parents('.form-group').addClass('has-error');
|
||||
$btn.button('reset');
|
||||
return false;
|
||||
}
|
||||
$("#btnSendMail").button("loading");
|
||||
},
|
||||
success : function (res) {
|
||||
|
||||
if(res.errcode !== 0){
|
||||
$("#captcha-img").click();
|
||||
$("#code").val('');
|
||||
layer.msg(res.message);
|
||||
$("#btnSendMail").button('reset');
|
||||
}else{
|
||||
alert("邮件发送成功,请登录邮箱查看。")
|
||||
window.location = res.data;
|
||||
}
|
||||
},
|
||||
error :function () {
|
||||
$("#captcha-img").click();
|
||||
$("#code").val('');
|
||||
layer.msg('系统错误');
|
||||
$("#btnSendMail").button('reset');
|
||||
}
|
||||
});
|
||||
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
144
views/account/find_password_setp2.tpl
Normal file
144
views/account/find_password_setp2.tpl
Normal file
@ -0,0 +1,144 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-cn">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<link rel="shortcut icon" href="/favicon.ico">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
|
||||
<meta name="renderer" content="webkit" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta name="author" content="SmartWiki" />
|
||||
<title>找回密码 - Powered by MinDoc</title>
|
||||
|
||||
<!-- Bootstrap -->
|
||||
<link href="/static/bootstrap/css/bootstrap.min.css" rel="stylesheet">
|
||||
<link href="/static/font-awesome/css/font-awesome.min.css" rel="stylesheet">
|
||||
<link href="/static/css/main.css" rel="stylesheet">
|
||||
<!-- HTML5 shim and Respond.js for IE8 support of HTML5 elements and media queries -->
|
||||
<!-- WARNING: Respond.js doesn't work if you view the page via file:// -->
|
||||
<!--[if lt IE 9]>
|
||||
<script src="/static/html5shiv/3.7.3/html5shiv.min.js"></script>
|
||||
<script src="/static/respond.js/1.4.2/respond.min.js"></script>
|
||||
<![endif]-->
|
||||
<!-- jQuery (necessary for Bootstrap's JavaScript plugins) -->
|
||||
<script src="/static/jquery/1.12.4/jquery.min.js"></script>
|
||||
</head>
|
||||
<body class="manual-container">
|
||||
<header class="navbar navbar-static-top smart-nav navbar-fixed-top manual-header" role="banner">
|
||||
<div class="container">
|
||||
<div class="navbar-header col-sm-12 col-md-6 col-lg-5">
|
||||
<a href="/" class="navbar-brand">{{.SITE_NAME}}</a>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
<div class="container manual-body">
|
||||
<div class="row login">
|
||||
<div class="login-body">
|
||||
<form role="form" method="post" id="findPasswordForm" action="{{urlfor "AccountController.ValidEmail"}}">
|
||||
<input type="hidden" name="token" value="{{.Token}}">
|
||||
<input type="hidden" name="mail" value="{{.Email}}">
|
||||
<h3 class="text-center">找回密码</h3>
|
||||
<div class="form-group">
|
||||
<label for="newPasswd">新密码</label>
|
||||
<input type="password" class="form-control" name="password1" id="newPassword" maxlength="20" placeholder="新密码" autocomplete="off">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="configPasswd">确认密码</label>
|
||||
<input type="password" class="form-control" id="confirmPassword" name="password2" maxlength="20" placeholder="确认密码" autocomplete="off">
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<div class="input-group" style="float: left;width: 195px;">
|
||||
<div class="input-group-addon">
|
||||
<i class="fa fa-check-square"></i>
|
||||
</div>
|
||||
<input type="text" name="code" id="code" class="form-control" style="width: 150px" maxlength="5" placeholder="验证码" autocomplete="off">
|
||||
</div>
|
||||
<img id="captcha-img" style="width: 140px;height: 40px;display: inline-block;float: right" src="{{urlfor "AccountController.Captcha"}}" onclick="this.src='{{urlfor "AccountController.Captcha"}}?key=login&t='+(new Date()).getTime();" title="点击换一张">
|
||||
<div class="clearfix"></div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<button type="submit" id="btnSendMail" class="btn btn-success" style="width: 100%" data-loading-text="正在处理..." autocomplete="off">找回密码</button>
|
||||
</div>
|
||||
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<div class="clearfix"></div>
|
||||
</div>
|
||||
{{template "widgets/footer.tpl" .}}
|
||||
<!-- Include all compiled plugins (below), or include individual files as needed -->
|
||||
<script src="/static/bootstrap/js/bootstrap.min.js" type="text/javascript"></script>
|
||||
<script src="/static/layer/layer.js" type="text/javascript"></script>
|
||||
<script src="/static/js/jquery.form.js" type="text/javascript"></script>
|
||||
<script type="text/javascript">
|
||||
$(function () {
|
||||
$("#email,#code").on('focus',function () {
|
||||
$(this).tooltip('destroy').parents('.form-group').removeClass('has-error');;
|
||||
});
|
||||
|
||||
$(document).keydown(function (e) {
|
||||
var event = document.all ? window.event : e;
|
||||
if(event.keyCode == 13){
|
||||
$("#btn-login").click();
|
||||
}
|
||||
});
|
||||
|
||||
$("#findPasswordForm").ajaxForm({
|
||||
beforeSubmit : function () {
|
||||
|
||||
var newPassword = $.trim($("#newPassword").val());
|
||||
var confirmPassword = $.trim($("#confirmPassword").val());
|
||||
var code = $.trim($("#code").val());
|
||||
|
||||
if(newPassword === ""){
|
||||
$("#newPassword").tooltip({placement:"auto",title : "密码不能为空",trigger : 'manual'})
|
||||
.tooltip('show')
|
||||
.parents('.form-group').addClass('has-error');
|
||||
|
||||
return false;
|
||||
|
||||
}else if(confirmPassword === ""){
|
||||
$("#confirmPassword").tooltip({placement:"auto",title : "确认密码不能为空",trigger : 'manual'})
|
||||
.tooltip('show')
|
||||
.parents('.form-group').addClass('has-error');
|
||||
|
||||
return false;
|
||||
}else if(newPassword !== confirmPassword) {
|
||||
$("#confirmPassword").tooltip({placement:"auto",title : "确认密码输入不正确",trigger : 'manual'})
|
||||
.tooltip('show')
|
||||
.parents('.form-group').addClass('has-error');
|
||||
|
||||
return false;
|
||||
}else if(code === ""){
|
||||
$("#code").tooltip({title : '验证码不能为空',trigger : 'manual'})
|
||||
.tooltip('show')
|
||||
.parents('.form-group').addClass('has-error');
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
$("#btnSendMail").button("loading");
|
||||
},
|
||||
success : function (res) {
|
||||
|
||||
if(res.errcode !== 0){
|
||||
$("#captcha-img").click();
|
||||
$("#code").val('');
|
||||
layer.msg(res.message);
|
||||
$("#btnSendMail").button('reset');
|
||||
}else{
|
||||
window.location = res.data;
|
||||
}
|
||||
},
|
||||
error :function () {
|
||||
$("#captcha-img").click();
|
||||
$("#code").val('');
|
||||
layer.msg('系统错误');
|
||||
$("#btnSendMail").button('reset');
|
||||
}
|
||||
});
|
||||
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
98
views/account/mail_template.tpl
Normal file
98
views/account/mail_template.tpl
Normal file
@ -0,0 +1,98 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta name="author" content="SmartWiki" />
|
||||
<meta http-equiv="Content-Type" content="text/html;charset=utf-8"/>
|
||||
<title>找回密码 - Powered by MinDoc</title>
|
||||
<style type="text/css">
|
||||
.ua-macos::-webkit-scrollbar{ display: none; }
|
||||
html,body{background-color: transparent;margin:0;padding: 0;}
|
||||
body{font: 16px/1.5 "Microsoft Yahei", "微软雅黑", verdana;word-wrap:break-word;}
|
||||
.js-dialog{font-size: 14px;}
|
||||
pre, .js-pre {
|
||||
white-space: pre-wrap;
|
||||
white-space: -moz-pre-wrap;
|
||||
white-space: -pre-wrap;
|
||||
white-space: -o-pre-wrap;
|
||||
word-wrap: break-word;
|
||||
font: 16px/1.5 "Microsoft Yahei", "微软雅黑", verdana;
|
||||
padding:8px 10px;margin:0;
|
||||
}
|
||||
.rm_line{border-top:2px solid #F1F1F1; font-size:0; margin:15px 0}
|
||||
.atchImg img{border:2px solid #c3d9ff;}
|
||||
.lnkTxt{ color:#0066CC}
|
||||
.rm_PicArea *{ font-family: "Microsoft Yahei", "微软雅黑", verdana;font-size:16px;font-weight:700;}
|
||||
.fbk3{ color:#333; line-height:160%}
|
||||
.fTip{ font-size:11px; font-weight:normal}
|
||||
|
||||
img{border:none;vertical-align: middle;}
|
||||
iframe{display:none;}
|
||||
*{word-break:break-word;}
|
||||
#neteaseEncryptedMail{display:none;}
|
||||
#jy-translate{
|
||||
position: absolute;
|
||||
max-width: 500px;
|
||||
min-width: 100px;
|
||||
_width:300px;
|
||||
border: 1px solid rgb(204, 204, 204);
|
||||
padding: 4px 18px 4px 10px;
|
||||
background-color: #f9f9f9;
|
||||
-webkit-border-radius:3px;
|
||||
-moz-border-radius:3px;
|
||||
border-radius:3px;
|
||||
-webkit-box-shadow:#dddddd 0px 0px 10px;
|
||||
-moz-box-shadow:#dddddd 0px 0px 10px;
|
||||
box-shadow:#dddddd 0px 0px 10px;
|
||||
}
|
||||
#jy-translate h2,
|
||||
#jy-translate p{color:#555;margin:0;padding:0;}
|
||||
#jy-translate h2{line-height: 28px;font-size: 14px;}
|
||||
#jy-translate p{line-height: 24px;font-size: 12px;}
|
||||
#jy-translate h2 span{font-weight:normal;}
|
||||
.ua-noyahei,
|
||||
.ua-noyahei .pre,
|
||||
.ua-noyahei .js-pre,
|
||||
.ua-noyahei .rm_PicArea *{font-family: \5b8b\4f53, sans-serif;}
|
||||
.ua-macos,
|
||||
.ua-macos .pre,
|
||||
.ua-macos .js-pre,
|
||||
.ua-macos .rm_PicArea *{font-family: "Lucida Grande","Hiragino Sans GB","Hiragino Sans GB W3", verdana;}
|
||||
|
||||
.jy-contact{float: left;}
|
||||
.jy-contact-hover{background: #eee;}
|
||||
.jy-contact img.oprt{width: 23px;height: 23px;border: 0;vertical-align: middle;cursor: pointer;}
|
||||
</style>
|
||||
</head>
|
||||
<body onunload="" class="js-body">
|
||||
<div>
|
||||
<div class="wrapper" style="margin: 20px auto 0; width: 500px; padding-top:16px; padding-bottom:10px;">
|
||||
<div class="header clearfix">
|
||||
<a class="logo" href="https://www.iminho.me/" target="_blank"><b>MinDoc</b></a>
|
||||
</div>
|
||||
<br style="clear:both; height:0">
|
||||
<div class="content" style="background: none repeat scroll 0 0 #FFFFFF; border: 1px solid #E9E9E9; margin: 2px 0 0; padding: 30px;">
|
||||
|
||||
<p>您好: </p>
|
||||
|
||||
<p>您在 {{.SITE_NAME}} 提交了找回密码申请。<br>如果您没有提交修改密码的申请, 请忽略本邮件</p>
|
||||
|
||||
<p style="border-top: 1px solid #DDDDDD;margin: 15px 0 25px;padding: 15px;">
|
||||
请点击链接继续: <a href="{{.url}}" target="_blank">{{.url}}</a>
|
||||
</p>
|
||||
<p>
|
||||
好的密码,不但应该容易记住,还要尽量符合以下强度标准:
|
||||
<ul>
|
||||
<li>包含大小写字母、数字和符号</li>
|
||||
<li>不少于 10 位 </li>
|
||||
<li>不包含生日、手机号码等易被猜出的信息</li>
|
||||
</ul>
|
||||
</p>
|
||||
<p class="footer" style="border-top: 1px solid #DDDDDD; padding-top:6px; margin-top:25px; color:#838383;">
|
||||
请勿回复本邮件, 此邮箱未受监控, 您不会得到任何回复. 要获得帮助, 请登录网站<br><br>
|
||||
<a href="https://www.iminho.me/" target="_blank">MinDoc</a>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
@ -110,6 +110,7 @@
|
||||
<div class="article-body {{if eq .Model.Editor "markdown"}}markdown-body editormd-preview-container{{else}}editor-content{{end}}" id="page-content">
|
||||
{{.Content}}
|
||||
</div>
|
||||
{{/*
|
||||
{{if .Model.IsDisplayComment}}
|
||||
<div id="articleComment" class="m-comment">
|
||||
<div class="comment-result">
|
||||
@ -154,6 +155,7 @@
|
||||
</div>
|
||||
</div>
|
||||
{{end}}
|
||||
*/}}
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
52
views/errors/403.tpl
Normal file
52
views/errors/403.tpl
Normal file
@ -0,0 +1,52 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="author" content="Minho" />
|
||||
<link rel="shortcut icon" href="/favicon.ico">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
|
||||
<meta name="renderer" content="webkit" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>权限不足 - Powered by MinDoc</title>
|
||||
<link href="/static/fonts/lato-100.css" rel="stylesheet" type="text/css">
|
||||
<style type="text/css">
|
||||
html, body {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
width: 100%;
|
||||
color: #B0BEC5;
|
||||
display: table;
|
||||
font-weight: 100;
|
||||
font-family: 'Lato',"Microsoft Yahei","Helvetica Neue",Helvetica,Arial,sans-serif;
|
||||
}
|
||||
|
||||
.container {
|
||||
text-align: center;
|
||||
display: table-cell;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.content {
|
||||
text-align: center;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.title {
|
||||
font-size: 72px;
|
||||
margin-bottom: 40px;
|
||||
color: red;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<div class="content">
|
||||
<div class="title">HTTP 403 : 权限不足</div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
51
views/errors/404.tpl
Normal file
51
views/errors/404.tpl
Normal file
@ -0,0 +1,51 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="author" content="Minho" />
|
||||
<link rel="shortcut icon" href="/favicon.ico">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
|
||||
<meta name="renderer" content="webkit" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>页面不存在 - Powered by MinDoc</title>
|
||||
<link href="/static/fonts/lato-100.css" rel="stylesheet" type="text/css">
|
||||
<style type="text/css">
|
||||
html, body {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
width: 100%;
|
||||
color: #B0BEC5;
|
||||
display: table;
|
||||
font-weight: 100;
|
||||
font-family: 'Lato',"Microsoft Yahei","Helvetica Neue",Helvetica,Arial,sans-serif;
|
||||
}
|
||||
|
||||
.container {
|
||||
text-align: center;
|
||||
display: table-cell;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.content {
|
||||
text-align: center;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.title {
|
||||
font-size: 72px;
|
||||
margin-bottom: 40px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<div class="content">
|
||||
<div class="title">HTTP 404 : 页面不存在.</div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
59
views/errors/error.tpl
Normal file
59
views/errors/error.tpl
Normal file
@ -0,0 +1,59 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="author" content="Minho" />
|
||||
<link rel="shortcut icon" href="/favicon.ico">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
|
||||
<meta name="renderer" content="webkit" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>服务器异常 - Powered by MinDoc</title>
|
||||
<link href="/static/fonts/lato-100.css" rel="stylesheet" type="text/css">
|
||||
<style type="text/css">
|
||||
html, body {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
width: 100%;
|
||||
color: #B0BEC5;
|
||||
display: table;
|
||||
font-weight: 100;
|
||||
font-family: 'Lato';
|
||||
}
|
||||
|
||||
.container {
|
||||
text-align: center;
|
||||
display: table-cell;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.content {
|
||||
text-align: center;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.title {
|
||||
font-size: 72px;
|
||||
margin-bottom: 40px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<div class="content">
|
||||
{{if .ErrorMessage}}
|
||||
{{if .ErrorCode}}
|
||||
<div class="title">HTTP {{.ErrorCode}} : {{.ErrorMessage}}</div>
|
||||
{{else}}
|
||||
<div class="title">HTTP 500 : {{.ErrorMessage}}</div>
|
||||
{{end}}
|
||||
{{else}}
|
||||
<div class="title">HTTP 500 : 服务器异常</div>
|
||||
{{end}}
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
Loading…
Reference in New Issue
Block a user