Merge branch 'master' into new-beego-path

pull/662/head
roberChen 2021-03-25 11:49:39 +08:00
commit 272316f7b4
17 changed files with 443 additions and 136 deletions

View File

@ -34,17 +34,17 @@ import (
// RegisterDataBase 注册数据库
func RegisterDataBase() {
logs.Info("正在初始化数据库配置.")
dbadapter,_ := web.AppConfig.String("db_adapter")
dbadapter, _ := web.AppConfig.String("db_adapter")
orm.DefaultTimeLoc = time.Local
orm.DefaultRowsLimit = -1
if strings.EqualFold(dbadapter, "mysql") {
host,_ := web.AppConfig.String("db_host")
host, _ := web.AppConfig.String("db_host")
database, _ := web.AppConfig.String("db_database")
username,_ := web.AppConfig.String("db_username")
password,_ := web.AppConfig.String("db_password")
username, _ := web.AppConfig.String("db_username")
password, _ := web.AppConfig.String("db_password")
timezone,_ := web.AppConfig.String("timezone")
timezone, _ := web.AppConfig.String("timezone")
location, err := time.LoadLocation(timezone)
if err == nil {
orm.DefaultTimeLoc = location
@ -52,7 +52,7 @@ func RegisterDataBase() {
logs.Error("加载时区配置信息失败,请检查是否存在 ZONEINFO 环境变量->", err)
}
port,_ := web.AppConfig.String("db_port")
port, _ := web.AppConfig.String("db_port")
dataSource := fmt.Sprintf("%s:%s@tcp(%s:%s)/%s?charset=utf8mb4&parseTime=true&loc=%s", username, password, host, port, database, url.QueryEscape(timezone))
@ -63,7 +63,7 @@ func RegisterDataBase() {
} else if strings.EqualFold(dbadapter, "sqlite3") {
database,_ := web.AppConfig.String("db_database")
database, _ := web.AppConfig.String("db_database")
if strings.HasPrefix(database, "./") {
database = filepath.Join(conf.WorkingDirectory, string(database[1:]))
}
@ -327,6 +327,7 @@ func ResolveCommand(args []string) {
web.BConfig.WebConfig.StaticDir["/static"] = filepath.Join(conf.WorkingDirectory, "static")
web.BConfig.WebConfig.StaticDir["/uploads"] = uploads
web.BConfig.WebConfig.ViewsPath = conf.WorkingDir("views")
web.BConfig.WebConfig.Session.SessionCookieSameSite = http.SameSiteDefaultMode
fonts := conf.WorkingDir("static", "fonts")

View File

@ -7,6 +7,7 @@ runmode = "${MINDOC_RUN_MODE||dev}"
sessionon = true
sessionname = mindoc_id
copyrequestbody = true
enablexsrf = "${MINDOC_ENABLE_XSRF||true}"
#系统完整URL(http://doc.iminho.me),如果该项不设置,会从请求头中获取地址。
baseurl="${MINDOC_BASE_URL}"
@ -209,6 +210,20 @@ log_level="${MINDOC_LOG_LEVEL||Alert}"
# 是否异步生成日志,默认是 true
log_is_async="${MINDOC_LOG_IS_ASYNC||TRUE}"
##########钉钉应用相关配置##############
# 企业钉钉ID
dingtalk_corpid="${MINDOC_DINGTALK_CORPID}"
# 钉钉AppKey
dingtalk_app_key="${MINDOC_DINGTALK_APPKEY}"
# 钉钉AppSecret
dingtalk_app_secret="${MINDOC_DINGTALK_APPSECRET}"
# 钉钉登录默认只读账号
dingtalk_tmp_reader="${MINDOC_DINGTALK_READER}"

View File

@ -9,11 +9,13 @@ import (
"html/template"
"github.com/beego/beego/v2/adapter/logs"
"github.com/beego/beego/v2/server/web"
"github.com/lifei6671/gocaptcha"
"github.com/mindoc-org/mindoc/conf"
"github.com/mindoc-org/mindoc/mail"
"github.com/mindoc-org/mindoc/models"
"github.com/mindoc-org/mindoc/utils"
"github.com/mindoc-org/mindoc/utils/dingtalk"
)
// AccountController 用户登录与注册
@ -31,8 +33,12 @@ func (c *AccountController) referer() string {
func (c *AccountController) Prepare() {
c.BaseController.Prepare()
c.EnableXSRF = true
c.EnableXSRF = web.AppConfig.DefaultBool("enablexsrf", true)
c.Data["xsrfdata"] = template.HTML(c.XSRFFormHTML())
c.Data["corpID"],_ = web.AppConfig.String("dingtalk_corpid")
if !c.EnableXSRF {
return
}
if c.Ctx.Input.IsPost() {
token := c.Ctx.Input.Query("_xsrf")
if token == "" {
@ -132,6 +138,60 @@ func (c *AccountController) Login() {
}
}
// 钉钉登录
func (c *AccountController) DingTalkLogin() {
c.Prepare()
code := c.GetString("dingtalk_code")
if code == "" {
c.JsonResult(500, "获取身份信息失败", nil)
}
appKey, _ := web.AppConfig.String("dingtalk_app_key")
appSecret, _ := web.AppConfig.String("dingtalk_app_secret")
tmpReader, _ := web.AppConfig.String("dingtalk_tmp_reader")
if appKey == "" || appSecret == "" || tmpReader == "" {
c.JsonResult(500, "未开启钉钉自动登录功能", nil)
c.StopRun()
}
dingtalkAgent := dingtalk.NewDingTalkAgent(appSecret, appKey)
err := dingtalkAgent.GetAccesstoken()
if err != nil {
logs.Warn("获取钉钉临时Token失败 ->", err)
c.JsonResult(500, "自动登录失败", nil)
c.StopRun()
}
userid, err := dingtalkAgent.GetUserIDByCode(code)
if err != nil {
logs.Warn("钉钉自动登录失败 ->", err)
c.JsonResult(500, "自动登录失败", nil)
c.StopRun()
}
username, avatar, err := dingtalkAgent.GetUserNameAndAvatarByUserID(userid)
if err != nil {
logs.Warn("钉钉自动登录失败 ->", err)
c.JsonResult(500, "自动登录失败", nil)
c.StopRun()
}
member, err := models.NewMember().TmpLogin(tmpReader)
if err == nil {
member.LastLoginTime = time.Now()
_ = member.Update("last_login_time")
member.Account = username
if avatar != "" {
member.Avatar = avatar
}
c.SetMember(*member)
}
c.JsonResult(0, "ok", username)
}
// 登录成功后的操作,如重定向到原始请求页面
func (c *AccountController) LoggedIn(isPost bool) interface{} {

2
go.mod
View File

@ -5,7 +5,7 @@ go 1.12
require (
github.com/PuerkitoBio/goquery v1.4.1
github.com/andybalholm/cascadia v1.2.0 // indirect
github.com/beego/beego/v2 v2.0.2-0.20210322114547-10ea897525a5 // indirect
github.com/beego/beego/v2 v2.0.2-0.20210322114547-10ea897525a5
github.com/boombuler/barcode v1.0.0
github.com/bradfitz/gomemcache v0.0.0-20190913173617-a41fca850d0b // indirect
github.com/howeyc/fsnotify v0.9.0

8
go.sum
View File

@ -11,9 +11,6 @@ github.com/alicebob/gopher-json v0.0.0-20180125190556-5a6b3ba71ee6/go.mod h1:SGn
github.com/alicebob/miniredis v2.5.0+incompatible/go.mod h1:8HZjEj4yU0dwhYHky+DxYx+6BMjkBbe5ONFIF1MXffk=
github.com/andybalholm/cascadia v1.2.0 h1:vuRCkM5Ozh/BfmsaTm26kbjm0mIOM3yS5Ek/F5h18aE=
github.com/andybalholm/cascadia v1.2.0/go.mod h1:YCyR8vOZT9aZ1CHEd8ap0gMVm2aFgxBp0T0eFw1RUQY=
github.com/beego/beego/v2 v2.0.0/go.mod h1:8zyHi1FnWO1mZLwTn62aKRIZF/aIKvkCBB2JYs+eqQI=
github.com/beego/beego/v2 v2.0.1 h1:07a7Z0Ok5vbqyqh+q53sDPl9LdhKh0ZDy3gbyGrhFnE=
github.com/beego/beego/v2 v2.0.1/go.mod h1:8zyHi1FnWO1mZLwTn62aKRIZF/aIKvkCBB2JYs+eqQI=
github.com/beego/beego/v2 v2.0.2-0.20210322114547-10ea897525a5 h1:i0swsv6hmoF6pkeG2S/dvOWwOKFPGDKhrmTlWH6I1nM=
github.com/beego/beego/v2 v2.0.2-0.20210322114547-10ea897525a5/go.mod h1:JlRUJ/NVNygorqjyt7/lQ8R++KSE0qXvxeIfbnIUd7Q=
github.com/beego/goyaml2 v0.0.0-20130207012346-5545475820dd/go.mod h1:1b+Y/CofkYwXMUU0OhQqGvsY2Bvgr4j6jfT699wyZKQ=
@ -37,11 +34,8 @@ github.com/coreos/etcd v3.3.25+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc
github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
github.com/coreos/go-systemd v0.0.0-20191104093116-d3cd4ed1dbcf/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
github.com/couchbase/go-couchbase v0.0.0-20200519150804-63f3cdb75e0d/go.mod h1:TWI8EKQMs5u5jLKW/tsb9VwauIrMIxQG1r5fMsswK5U=
github.com/couchbase/go-couchbase v0.0.0-20210126152612-8e416c37c8ef/go.mod h1:+/bddYDxXsf9qt0xpDUtRR47A2GjaXmGGAqQ/k3GJ8A=
github.com/couchbase/gomemcached v0.0.0-20200526233749-ec430f949808/go.mod h1:srVSlQLB8iXBVXHgnqemxUXqN6FCvClgCMPCsjBDR7c=
github.com/couchbase/gomemcached v0.1.2-0.20210126151728-840240974836/go.mod h1:mxliKQxOv84gQ0bJWbI+w9Wxdpt9HjDvgW9MjCym5Vo=
github.com/couchbase/goutils v0.0.0-20180530154633-e865a1461c8a/go.mod h1:BQwMFlJzDjFDG3DJUdU0KORxn88UlsOULuxLExMh3Hs=
github.com/couchbase/goutils v0.0.0-20201030094643-5e82bb967e67/go.mod h1:BQwMFlJzDjFDG3DJUdU0KORxn88UlsOULuxLExMh3Hs=
github.com/cupcake/rdb v0.0.0-20161107195141-43ba34106c76/go.mod h1:vYwsqCOLxGiisLwp9rITslkFNpZD5rz43tf41QFkTWY=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
@ -216,7 +210,6 @@ golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHl
golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
golang.org/x/mod v0.3.0 h1:RM4zey1++hCTbCVQfnWeKs9/IEsaBLA8vTkd0WVtmH4=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/net v0.0.0-20180218175443-cbe0f9307d01/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
@ -268,7 +261,6 @@ golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtn
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20201211185031-d93e913c1a58 h1:1Bs6RVeBFtLZ8Yi1Hk07DiOqzvwLD/4hln4iahvFlag=
golang.org/x/tools v0.0.0-20201211185031-d93e913c1a58/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=

View File

@ -19,6 +19,8 @@ var (
ErrorMemberPasswordError = errors.New("用户密码错误")
//ErrorMemberAuthMethodInvalid 不支持此认证方式
ErrMemberAuthMethodInvalid = errors.New("不支持此认证方式")
//ErrHTTPServerFail
ErrHTTPServerFail = errors.New("系统内部异常")
//ErrLDAPConnect 无法连接到LDAP服务器
ErrLDAPConnect = errors.New("无法连接到LDAP服务器")
//ErrLDAPFirstBind 第一次LDAP绑定失败

View File

@ -105,6 +105,17 @@ func (m *Member) Login(account string, password string) (*Member, error) {
return member, ErrorMemberPasswordError
}
// TmpLogin 用于钉钉临时登录
func (m *Member) TmpLogin(account string) (*Member, error) {
o := orm.NewOrm()
member := &Member{}
err := o.Raw("select * from md_members where account = ? and status = 0 limit 1;", account).QueryRow(member)
if err != nil {
return member, ErrorMemberPasswordError
}
return member, nil
}
//ldapLogin 通过LDAP登陆
func (m *Member) ldapLogin(account string, password string) (*Member, error) {
if web.AppConfig.DefaultBool("ldap_enable", false) {
@ -187,14 +198,14 @@ func (m *Member) httpLogin(account, password string) (*Member, error) {
resp, err := http.PostForm(urlStr, val)
if err != nil {
logs.Error("通过接口登录失败 -> ", urlStr, account, err)
return nil, err
return nil, ErrHTTPServerFail
}
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
logs.Error("读取接口返回值失败 -> ", urlStr, account, err)
return nil, err
return nil, ErrHTTPServerFail
}
logs.Info("HTTP 登录接口返回数据 ->", string(body))
@ -202,7 +213,7 @@ func (m *Member) httpLogin(account, password string) (*Member, error) {
if err := json.Unmarshal(body, &result); err != nil {
logs.Error("解析接口返回值失败 -> ", urlStr, account, string(body))
return nil, errors.New("解析接口返回值失败")
return nil, ErrHTTPServerFail
}
if code, ok := result["errcode"]; !ok || code.(float64) != 200 {
@ -210,7 +221,7 @@ func (m *Member) httpLogin(account, password string) (*Member, error) {
if msg, ok := result["message"]; ok {
return nil, errors.New(msg.(string))
}
return nil, errors.New("接口返回值格式不正确")
return nil, ErrHTTPServerFail
}
if m.MemberId <= 0 {
member := NewMember()

View File

@ -205,16 +205,16 @@ func (m *TeamMember) FindNotJoinMemberByAccount(teamId int, account string, limi
}
o := orm.NewOrm()
sql := `select member.member_id,member.account,team.team_member_id
sql := `select member.member_id,member.account,member.real_name,team.team_member_id
from md_members as member
left join md_team_member as team on team.team_id = ? and member.member_id = team.member_id
where member.account like ? AND team_member_id IS NULL
where member.account like ? or member.real_name like ? AND team_member_id IS NULL
order by member.member_id desc
limit ?;`
members := make([]*Member, 0)
_, err := o.Raw(sql, teamId, "%"+account+"%", limit).QueryRows(&members)
_, err := o.Raw(sql, teamId, "%"+account+"%", "%"+account+"%", limit).QueryRows(&members)
if err != nil {
logs.Error("查询团队用户时出错 ->", err)
@ -227,7 +227,7 @@ limit ?;`
for _, member := range members {
item := KeyValueItem{}
item.Id = member.MemberId
item.Text = member.Account
item.Text = member.Account + "[" + member.RealName + "]"
items = append(items, item)
}
result.Result = items

View File

@ -1,146 +1,147 @@
package routers
import (
"github.com/beego/beego/v2/adapter"
"github.com/beego/beego/v2/server/web"
"github.com/mindoc-org/mindoc/controllers"
)
func init() {
adapter.Router("/", &controllers.HomeController{}, "*:Index")
web.Router("/", &controllers.HomeController{}, "*:Index")
adapter.Router("/login", &controllers.AccountController{}, "*:Login")
adapter.Router("/logout", &controllers.AccountController{}, "*:Logout")
adapter.Router("/register", &controllers.AccountController{}, "*:Register")
adapter.Router("/find_password", &controllers.AccountController{}, "*:FindPassword")
adapter.Router("/valid_email", &controllers.AccountController{}, "post:ValidEmail")
adapter.Router("/captcha", &controllers.AccountController{}, "*:Captcha")
web.Router("/login", &controllers.AccountController{}, "*:Login")
web.Router("/dingtalk_login", &controllers.AccountController{}, "*:DingTalkLogin")
web.Router("/logout", &controllers.AccountController{}, "*:Logout")
web.Router("/register", &controllers.AccountController{}, "*:Register")
web.Router("/find_password", &controllers.AccountController{}, "*:FindPassword")
web.Router("/valid_email", &controllers.AccountController{}, "post:ValidEmail")
web.Router("/captcha", &controllers.AccountController{}, "*:Captcha")
adapter.Router("/manager", &controllers.ManagerController{}, "*:Index")
adapter.Router("/manager/users", &controllers.ManagerController{}, "*:Users")
adapter.Router("/manager/users/edit/:id", &controllers.ManagerController{}, "*:EditMember")
adapter.Router("/manager/member/create", &controllers.ManagerController{}, "post:CreateMember")
adapter.Router("/manager/member/delete", &controllers.ManagerController{}, "post:DeleteMember")
adapter.Router("/manager/member/update-member-status", &controllers.ManagerController{}, "post:UpdateMemberStatus")
adapter.Router("/manager/member/change-member-role", &controllers.ManagerController{}, "post:ChangeMemberRole")
adapter.Router("/manager/books", &controllers.ManagerController{}, "*:Books")
adapter.Router("/manager/books/edit/:key", &controllers.ManagerController{}, "*:EditBook")
adapter.Router("/manager/books/delete", &controllers.ManagerController{}, "*:DeleteBook")
web.Router("/manager", &controllers.ManagerController{}, "*:Index")
web.Router("/manager/users", &controllers.ManagerController{}, "*:Users")
web.Router("/manager/users/edit/:id", &controllers.ManagerController{}, "*:EditMember")
web.Router("/manager/member/create", &controllers.ManagerController{}, "post:CreateMember")
web.Router("/manager/member/delete", &controllers.ManagerController{}, "post:DeleteMember")
web.Router("/manager/member/update-member-status", &controllers.ManagerController{}, "post:UpdateMemberStatus")
web.Router("/manager/member/change-member-role", &controllers.ManagerController{}, "post:ChangeMemberRole")
web.Router("/manager/books", &controllers.ManagerController{}, "*:Books")
web.Router("/manager/books/edit/:key", &controllers.ManagerController{}, "*:EditBook")
web.Router("/manager/books/delete", &controllers.ManagerController{}, "*:DeleteBook")
adapter.Router("/manager/comments", &controllers.ManagerController{}, "*:Comments")
adapter.Router("/manager/setting", &controllers.ManagerController{}, "*:Setting")
adapter.Router("/manager/books/token", &controllers.ManagerController{}, "post:CreateToken")
adapter.Router("/manager/books/transfer", &controllers.ManagerController{}, "post:Transfer")
adapter.Router("/manager/books/open", &controllers.ManagerController{}, "post:PrivatelyOwned")
web.Router("/manager/comments", &controllers.ManagerController{}, "*:Comments")
web.Router("/manager/setting", &controllers.ManagerController{}, "*:Setting")
web.Router("/manager/books/token", &controllers.ManagerController{}, "post:CreateToken")
web.Router("/manager/books/transfer", &controllers.ManagerController{}, "post:Transfer")
web.Router("/manager/books/open", &controllers.ManagerController{}, "post:PrivatelyOwned")
adapter.Router("/manager/attach/list", &controllers.ManagerController{}, "*:AttachList")
adapter.Router("/manager/attach/detailed/:id", &controllers.ManagerController{}, "*:AttachDetailed")
adapter.Router("/manager/attach/delete", &controllers.ManagerController{}, "post:AttachDelete")
adapter.Router("/manager/label/list", &controllers.ManagerController{}, "get:LabelList")
adapter.Router("/manager/label/delete/:id", &controllers.ManagerController{}, "post:LabelDelete")
web.Router("/manager/attach/list", &controllers.ManagerController{}, "*:AttachList")
web.Router("/manager/attach/detailed/:id", &controllers.ManagerController{}, "*:AttachDetailed")
web.Router("/manager/attach/delete", &controllers.ManagerController{}, "post:AttachDelete")
web.Router("/manager/label/list", &controllers.ManagerController{}, "get:LabelList")
web.Router("/manager/label/delete/:id", &controllers.ManagerController{}, "post:LabelDelete")
//adapter.Router("/manager/config", &controllers.ManagerController{}, "*:Config")
//web.Router("/manager/config", &controllers.ManagerController{}, "*:Config")
adapter.Router("/manager/team", &controllers.ManagerController{}, "*:Team")
adapter.Router("/manager/team/create", &controllers.ManagerController{}, "POST:TeamCreate")
adapter.Router("/manager/team/edit", &controllers.ManagerController{}, "POST:TeamEdit")
adapter.Router("/manager/team/delete", &controllers.ManagerController{}, "POST:TeamDelete")
web.Router("/manager/team", &controllers.ManagerController{}, "*:Team")
web.Router("/manager/team/create", &controllers.ManagerController{}, "POST:TeamCreate")
web.Router("/manager/team/edit", &controllers.ManagerController{}, "POST:TeamEdit")
web.Router("/manager/team/delete", &controllers.ManagerController{}, "POST:TeamDelete")
adapter.Router("/manager/team/member/list/:id", &controllers.ManagerController{}, "*:TeamMemberList")
adapter.Router("/manager/team/member/add", &controllers.ManagerController{}, "POST:TeamMemberAdd")
adapter.Router("/manager/team/member/delete", &controllers.ManagerController{}, "POST:TeamMemberDelete")
adapter.Router("/manager/team/member/change_role", &controllers.ManagerController{}, "POST:TeamChangeMemberRole")
adapter.Router("/manager/team/member/search", &controllers.ManagerController{}, "*:TeamSearchMember")
web.Router("/manager/team/member/list/:id", &controllers.ManagerController{}, "*:TeamMemberList")
web.Router("/manager/team/member/add", &controllers.ManagerController{}, "POST:TeamMemberAdd")
web.Router("/manager/team/member/delete", &controllers.ManagerController{}, "POST:TeamMemberDelete")
web.Router("/manager/team/member/change_role", &controllers.ManagerController{}, "POST:TeamChangeMemberRole")
web.Router("/manager/team/member/search", &controllers.ManagerController{}, "*:TeamSearchMember")
adapter.Router("/manager/team/book/list/:id", &controllers.ManagerController{}, "*:TeamBookList")
adapter.Router("/manager/team/book/add", &controllers.ManagerController{}, "POST:TeamBookAdd")
adapter.Router("/manager/team/book/delete", &controllers.ManagerController{}, "POST:TeamBookDelete")
adapter.Router("/manager/team/book/search", &controllers.ManagerController{}, "*:TeamSearchBook")
web.Router("/manager/team/book/list/:id", &controllers.ManagerController{}, "*:TeamBookList")
web.Router("/manager/team/book/add", &controllers.ManagerController{}, "POST:TeamBookAdd")
web.Router("/manager/team/book/delete", &controllers.ManagerController{}, "POST:TeamBookDelete")
web.Router("/manager/team/book/search", &controllers.ManagerController{}, "*:TeamSearchBook")
adapter.Router("/manager/itemsets", &controllers.ManagerController{}, "*:Itemsets")
adapter.Router("/manager/itemsets/edit", &controllers.ManagerController{}, "post:ItemsetsEdit")
adapter.Router("/manager/itemsets/delete", &controllers.ManagerController{}, "post:ItemsetsDelete")
web.Router("/manager/itemsets", &controllers.ManagerController{}, "*:Itemsets")
web.Router("/manager/itemsets/edit", &controllers.ManagerController{}, "post:ItemsetsEdit")
web.Router("/manager/itemsets/delete", &controllers.ManagerController{}, "post:ItemsetsDelete")
adapter.Router("/setting", &controllers.SettingController{}, "*:Index")
adapter.Router("/setting/password", &controllers.SettingController{}, "*:Password")
adapter.Router("/setting/upload", &controllers.SettingController{}, "*:Upload")
web.Router("/setting", &controllers.SettingController{}, "*:Index")
web.Router("/setting/password", &controllers.SettingController{}, "*:Password")
web.Router("/setting/upload", &controllers.SettingController{}, "*:Upload")
adapter.Router("/book", &controllers.BookController{}, "*:Index")
adapter.Router("/book/:key/dashboard", &controllers.BookController{}, "*:Dashboard")
adapter.Router("/book/:key/setting", &controllers.BookController{}, "*:Setting")
adapter.Router("/book/:key/users", &controllers.BookController{}, "*:Users")
adapter.Router("/book/:key/release", &controllers.BookController{}, "post:Release")
adapter.Router("/book/:key/sort", &controllers.BookController{}, "post:SaveSort")
adapter.Router("/book/:key/teams", &controllers.BookController{}, "*:Team")
web.Router("/book", &controllers.BookController{}, "*:Index")
web.Router("/book/:key/dashboard", &controllers.BookController{}, "*:Dashboard")
web.Router("/book/:key/setting", &controllers.BookController{}, "*:Setting")
web.Router("/book/:key/users", &controllers.BookController{}, "*:Users")
web.Router("/book/:key/release", &controllers.BookController{}, "post:Release")
web.Router("/book/:key/sort", &controllers.BookController{}, "post:SaveSort")
web.Router("/book/:key/teams", &controllers.BookController{}, "*:Team")
adapter.Router("/book/create", &controllers.BookController{}, "*:Create")
adapter.Router("/book/itemsets/search", &controllers.BookController{}, "*:ItemsetsSearch")
web.Router("/book/create", &controllers.BookController{}, "*:Create")
web.Router("/book/itemsets/search", &controllers.BookController{}, "*:ItemsetsSearch")
adapter.Router("/book/users/create", &controllers.BookMemberController{}, "post:AddMember")
adapter.Router("/book/users/change", &controllers.BookMemberController{}, "post:ChangeRole")
adapter.Router("/book/users/delete", &controllers.BookMemberController{}, "post:RemoveMember")
adapter.Router("/book/users/import", &controllers.BookController{}, "post:Import")
adapter.Router("/book/users/copy", &controllers.BookController{}, "post:Copy")
web.Router("/book/users/create", &controllers.BookMemberController{}, "post:AddMember")
web.Router("/book/users/change", &controllers.BookMemberController{}, "post:ChangeRole")
web.Router("/book/users/delete", &controllers.BookMemberController{}, "post:RemoveMember")
web.Router("/book/users/import", &controllers.BookController{}, "post:Import")
web.Router("/book/users/copy", &controllers.BookController{}, "post:Copy")
adapter.Router("/book/setting/save", &controllers.BookController{}, "post:SaveBook")
adapter.Router("/book/setting/open", &controllers.BookController{}, "post:PrivatelyOwned")
adapter.Router("/book/setting/transfer", &controllers.BookController{}, "post:Transfer")
adapter.Router("/book/setting/upload", &controllers.BookController{}, "post:UploadCover")
adapter.Router("/book/setting/delete", &controllers.BookController{}, "post:Delete")
web.Router("/book/setting/save", &controllers.BookController{}, "post:SaveBook")
web.Router("/book/setting/open", &controllers.BookController{}, "post:PrivatelyOwned")
web.Router("/book/setting/transfer", &controllers.BookController{}, "post:Transfer")
web.Router("/book/setting/upload", &controllers.BookController{}, "post:UploadCover")
web.Router("/book/setting/delete", &controllers.BookController{}, "post:Delete")
adapter.Router("/book/team/add", &controllers.BookController{}, "POST:TeamAdd")
adapter.Router("/book/team/delete", &controllers.BookController{}, "POST:TeamDelete")
adapter.Router("/book/team/search", &controllers.BookController{}, "*:TeamSearch")
web.Router("/book/team/add", &controllers.BookController{}, "POST:TeamAdd")
web.Router("/book/team/delete", &controllers.BookController{}, "POST:TeamDelete")
web.Router("/book/team/search", &controllers.BookController{}, "*:TeamSearch")
//管理文章的路由
adapter.Router("/manage/blogs", &controllers.BlogController{}, "*:ManageList")
adapter.Router("/manage/blogs/setting/?:id", &controllers.BlogController{}, "*:ManageSetting")
adapter.Router("/manage/blogs/edit/?:id", &controllers.BlogController{}, "*:ManageEdit")
adapter.Router("/manage/blogs/delete", &controllers.BlogController{}, "post:ManageDelete")
adapter.Router("/manage/blogs/upload", &controllers.BlogController{}, "post:Upload")
adapter.Router("/manage/blogs/attach/:id", &controllers.BlogController{}, "post:RemoveAttachment")
web.Router("/manage/blogs", &controllers.BlogController{}, "*:ManageList")
web.Router("/manage/blogs/setting/?:id", &controllers.BlogController{}, "*:ManageSetting")
web.Router("/manage/blogs/edit/?:id", &controllers.BlogController{}, "*:ManageEdit")
web.Router("/manage/blogs/delete", &controllers.BlogController{}, "post:ManageDelete")
web.Router("/manage/blogs/upload", &controllers.BlogController{}, "post:Upload")
web.Router("/manage/blogs/attach/:id", &controllers.BlogController{}, "post:RemoveAttachment")
//读文章的路由
adapter.Router("/blogs", &controllers.BlogController{}, "*:List")
adapter.Router("/blog-attach/:id:int/:attach_id:int", &controllers.BlogController{}, "get:Download")
adapter.Router("/blog-:id([0-9]+).html", &controllers.BlogController{}, "*:Index")
web.Router("/blogs", &controllers.BlogController{}, "*:List")
web.Router("/blog-attach/:id:int/:attach_id:int", &controllers.BlogController{}, "get:Download")
web.Router("/blog-:id([0-9]+).html", &controllers.BlogController{}, "*:Index")
//模板相关接口
adapter.Router("/api/template/get", &controllers.TemplateController{}, "get:Get")
adapter.Router("/api/template/list", &controllers.TemplateController{}, "post:List")
adapter.Router("/api/template/add", &controllers.TemplateController{}, "post:Add")
adapter.Router("/api/template/remove", &controllers.TemplateController{}, "post:Delete")
web.Router("/api/template/get", &controllers.TemplateController{}, "get:Get")
web.Router("/api/template/list", &controllers.TemplateController{}, "post:List")
web.Router("/api/template/add", &controllers.TemplateController{}, "post:Add")
web.Router("/api/template/remove", &controllers.TemplateController{}, "post:Delete")
adapter.Router("/api/attach/remove/", &controllers.DocumentController{}, "post:RemoveAttachment")
adapter.Router("/api/:key/edit/?:id", &controllers.DocumentController{}, "*:Edit")
adapter.Router("/api/upload", &controllers.DocumentController{}, "post:Upload")
adapter.Router("/api/:key/create", &controllers.DocumentController{}, "post:Create")
adapter.Router("/api/:key/delete", &controllers.DocumentController{}, "post:Delete")
adapter.Router("/api/:key/content/?:id", &controllers.DocumentController{}, "*:Content")
adapter.Router("/api/:key/compare/:id", &controllers.DocumentController{}, "*:Compare")
adapter.Router("/api/search/user/:key", &controllers.SearchController{}, "*:User")
web.Router("/api/attach/remove/", &controllers.DocumentController{}, "post:RemoveAttachment")
web.Router("/api/:key/edit/?:id", &controllers.DocumentController{}, "*:Edit")
web.Router("/api/upload", &controllers.DocumentController{}, "post:Upload")
web.Router("/api/:key/create", &controllers.DocumentController{}, "post:Create")
web.Router("/api/:key/delete", &controllers.DocumentController{}, "post:Delete")
web.Router("/api/:key/content/?:id", &controllers.DocumentController{}, "*:Content")
web.Router("/api/:key/compare/:id", &controllers.DocumentController{}, "*:Compare")
web.Router("/api/search/user/:key", &controllers.SearchController{}, "*:User")
adapter.Router("/history/get", &controllers.DocumentController{}, "get:History")
adapter.Router("/history/delete", &controllers.DocumentController{}, "*:DeleteHistory")
adapter.Router("/history/restore", &controllers.DocumentController{}, "*:RestoreHistory")
web.Router("/history/get", &controllers.DocumentController{}, "get:History")
web.Router("/history/delete", &controllers.DocumentController{}, "*:DeleteHistory")
web.Router("/history/restore", &controllers.DocumentController{}, "*:RestoreHistory")
adapter.Router("/docs/:key", &controllers.DocumentController{}, "*:Index")
adapter.Router("/docs/:key/:id", &controllers.DocumentController{}, "*:Read")
adapter.Router("/docs/:key/search", &controllers.DocumentController{}, "post:Search")
adapter.Router("/export/:key", &controllers.DocumentController{}, "*:Export")
adapter.Router("/qrcode/:key.png", &controllers.DocumentController{}, "get:QrCode")
web.Router("/docs/:key", &controllers.DocumentController{}, "*:Index")
web.Router("/docs/:key/:id", &controllers.DocumentController{}, "*:Read")
web.Router("/docs/:key/search", &controllers.DocumentController{}, "post:Search")
web.Router("/export/:key", &controllers.DocumentController{}, "*:Export")
web.Router("/qrcode/:key.png", &controllers.DocumentController{}, "get:QrCode")
adapter.Router("/attach_files/:key/:attach_id", &controllers.DocumentController{}, "get:DownloadAttachment")
web.Router("/attach_files/:key/:attach_id", &controllers.DocumentController{}, "get:DownloadAttachment")
adapter.Router("/comment/create", &controllers.CommentController{}, "post:Create")
adapter.Router("/comment/lists", &controllers.CommentController{}, "get:Lists")
adapter.Router("/comment/index", &controllers.CommentController{}, "*:Index")
web.Router("/comment/create", &controllers.CommentController{}, "post:Create")
web.Router("/comment/lists", &controllers.CommentController{}, "get:Lists")
web.Router("/comment/index", &controllers.CommentController{}, "*:Index")
adapter.Router("/search", &controllers.SearchController{}, "get:Index")
web.Router("/search", &controllers.SearchController{}, "get:Index")
adapter.Router("/tag/:key", &controllers.LabelController{}, "get:Index")
adapter.Router("/tags", &controllers.LabelController{}, "get:List")
web.Router("/tag/:key", &controllers.LabelController{}, "get:Index")
web.Router("/tags", &controllers.LabelController{}, "get:List")
adapter.Router("/items", &controllers.ItemsetsController{}, "get:Index")
adapter.Router("/items/:key", &controllers.ItemsetsController{}, "get:List")
web.Router("/items", &controllers.ItemsetsController{}, "get:Index")
web.Router("/items/:key", &controllers.ItemsetsController{}, "get:List")
}

View File

@ -1037,6 +1037,24 @@ textarea{
margin-left: 0;
}
}
.navbar-mobile {
display: inline-block;
padding: 10px 0;
font-size: 14px;
line-height: 30px;
color: #563d7c;
}
.navbar-mobile a {
padding-right: 5px;
}
@media (min-width: 768px) {
.navbar-mobile {
display: none !important;
}
}

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,147 @@
package dingtalk
import (
"crypto/hmac"
"crypto/sha256"
"encoding/base64"
"encoding/json"
"errors"
"fmt"
"io/ioutil"
"net/http"
"net/url"
)
// DingTalkAgent 用于钉钉交互
type DingTalkAgent struct {
AppSecret string
AppKey string
AccessToken string
}
// NewDingTalkAgent 钉钉交互构造函数
func NewDingTalkAgent(appSecret, appKey string) *DingTalkAgent {
return &DingTalkAgent{
AppSecret: appSecret,
AppKey: appKey,
}
}
// GetUserIDByCode 通过临时code获取当前用户ID
func (d *DingTalkAgent) GetUserIDByCode(code string) (string, error) {
urlEndpoint, err := url.Parse("https://oapi.dingtalk.com/user/getuserinfo")
if err != nil {
return "", err
}
query := url.Values{}
query.Set("access_token", d.AccessToken)
query.Set("code", code)
urlEndpoint.RawQuery = query.Encode()
urlPath := urlEndpoint.String()
resp, err := http.Get(urlPath)
if err != nil {
return "", err
}
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
return "", err
}
// 解析钉钉返回数据
var rdata map[string]interface{}
err = json.Unmarshal(body, &rdata)
if err != nil {
return "", err
}
errcode := rdata["errcode"].(float64)
if errcode != 0 {
return "", errors.New(fmt.Sprintf("登录错误: %.0f, %s", errcode, rdata["errmsg"].(string)))
}
userid := rdata["userid"].(string)
return userid, nil
}
// GetUserNameAndAvatarByUserID 通过userid获取当前用户姓名和头像
func (d *DingTalkAgent) GetUserNameAndAvatarByUserID(userid string) (string, string, error) {
urlEndpoint, err := url.Parse("https://oapi.dingtalk.com/topapi/v2/user/get")
if err != nil {
return "", "", err
}
query := url.Values{}
query.Set("access_token", d.AccessToken)
urlEndpoint.RawQuery = query.Encode()
urlPath := urlEndpoint.String()
resp, err := http.PostForm(urlPath, url.Values{"userid": {userid}})
if err != nil {
return "", "", err
}
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
return "", "", err
}
// 解析钉钉返回数据
var rdata map[string]interface{}
err = json.Unmarshal(body, &rdata)
if err != nil {
return "", "", err
}
errcode := rdata["errcode"].(float64)
if errcode != 0 {
return "", "", errors.New(fmt.Sprintf("登录错误: %.0f, %s", errcode, rdata["errmsg"].(string)))
}
userinfo := rdata["result"].(map[string]interface{})
username := userinfo["name"].(string)
avatar := userinfo["avatar"].(string)
return username, avatar, nil
}
// GetAccesstoken 获取钉钉请求Token
func (d *DingTalkAgent) GetAccesstoken() (err error) {
url := fmt.Sprintf("https://oapi.dingtalk.com/gettoken?appkey=%s&appsecret=%s", d.AppKey, d.AppSecret)
resp, err := http.Get(url)
if err != nil {
return err
}
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
return err
}
var i map[string]interface{}
err = json.Unmarshal(body, &i)
if err != nil {
return err
}
if i["errcode"].(float64) == 0 {
d.AccessToken = i["access_token"].(string)
return nil
}
return errors.New("accesstoken获取错误" + i["errmsg"].(string))
}
func (d *DingTalkAgent) encodeSHA256(message string) string {
// 钉钉签名算法实现
h := hmac.New(sha256.New, []byte(d.AppSecret))
h.Write([]byte(message))
sum := h.Sum(nil) // 二进制流
tmpMsg := base64.StdEncoding.EncodeToString(sum)
uv := url.Values{}
uv.Add("0", tmpMsg)
message = uv.Encode()[2:]
return message
}

View File

@ -86,6 +86,47 @@
<!-- Include all compiled plugins (below), or include individual files as needed -->
<script src="{{cdnjs "/static/bootstrap/js/bootstrap.min.js"}}" type="text/javascript"></script>
<script src="{{cdnjs "/static/layer/layer.js"}}" type="text/javascript"></script>
<script src="{{cdnjs "/static/js/dingtalk-jsapi.js"}}" type="text/javascript"></script>
<script type="text/javascript">
$(function () {
if (dd.env.platform !== "notInDingTalk"){
dd.ready(function() {
dd.runtime.permission.requestAuthCode({
corpId: {{ .corpID }} , // 企业id
onSuccess: function (info) {
var index = layer.load(1, {
shade: [0.1, '#fff'] // 0.1 透明度的白色背景
})
var formData = $("form").serializeArray()
formData.push({"name": "dingtalk_code", "value": info.code})
$.ajax({
url: "{{urlfor "AccountController.DingTalkLogin"}} ",
data: formData,
dataType: "json",
type: "POST",
complete: function(){
layer.close(index)
},
success: function (res) {
if (res.errcode !== 0) {
layer.msg(res.message)
} else {
window.location = "{{ urlfor "HomeController.Index" }}"
}
},
error: function (res) {
layer.msg("发生异常")
}
})
}
});
});
}
})
</script>
<script type="text/javascript">
$(function () {
$("#account,#password,#code").on('focus', function () {

View File

@ -30,7 +30,7 @@
<div class="manual-list">
{{range $index,$item := .Lists}}
<div class="search-item">
<div class="title">{{if eq $item.BlogStatus "password"}}<span class="label">密</span>{{end}} <a href="{{urlfor "BlogController.Index" ":id" $item.BlogId}}" title="{{$item.BlogTitle}}" target="_blank">{{$item.BlogTitle}}</a> </div>
<div class="title">{{if eq $item.BlogStatus "password"}}<span class="label">密</span>{{end}} <a href="{{urlfor "BlogController.Index" ":id" $item.BlogId}}" title="{{$item.BlogTitle}}">{{$item.BlogTitle}}</a> </div>
<div class="description">
{{$item.BlogExcerpt}}
</div>

View File

@ -30,6 +30,19 @@
<link href="{{cdncss "/static/css/print.css" "version"}}" media="print" rel="stylesheet">
<script type="text/javascript">window.book={"identify":"{{.Model.Identify}}"};</script>
<style>
.btn-mobile {
position: absolute;
right: 10px;
top: 10px;
}
@media screen and (min-width: 840px) {
.btn-mobile{
display: none;
}
}
</style>
</head>
<body>
<div class="m-manual manual-mode-view manual-reader">
@ -40,6 +53,7 @@
<a href="{{urlfor "DocumentController.Index" ":key" .Model.Identify}}" title="{{.Model.BookName}}" class="book-title">{{.Model.BookName}}</a>
<span style="font-size: 12px;font-weight: 100;"></span>
</div>
<a href="{{urlfor "HomeController.Index"}}" class="btn btn-default btn-mobile"> <i class="fa fa-home" aria-hidden="true"></i>首页</a>
<div class="navbar-header pull-right manual-menu">
<a href="javascript:window.print();" id="printSinglePage" class="btn btn-default" style="margin-right: 10px;"><i class="fa fa-print"></i> 打印</a>
{{if gt .Member.MemberId 0}}

View File

@ -26,12 +26,12 @@
<div class="list-item">
<dl class="manual-item-standard">
<dt>
<a href="{{urlfor "DocumentController.Index" ":key" $item.Identify}}" title="{{$item.BookName}}-{{$item.CreateName}}" target="_blank">
<a href="{{urlfor "DocumentController.Index" ":key" $item.Identify}}" title="{{$item.BookName}}-{{$item.CreateName}}">
<img src="{{cdnimg $item.Cover}}" class="cover" alt="{{$item.BookName}}-{{$item.CreateName}}" onerror="this.src='{{cdnimg "static/images/book.jpg"}}';">
</a>
</dt>
<dd>
<a href="{{urlfor "DocumentController.Index" ":key" $item.Identify}}" class="name" title="{{$item.BookName}}-{{$item.CreateName}}" target="_blank">{{$item.BookName}}</a>
<a href="{{urlfor "DocumentController.Index" ":key" $item.Identify}}" class="name" title="{{$item.BookName}}-{{$item.CreateName}}">{{$item.BookName}}</a>
</dd>
<dd>
<span class="author">

View File

@ -29,6 +29,10 @@
</form>
</div>
</nav>
<div style="display: inline-block;" class="navbar-mobile">
<a href="{{urlfor "HomeController.Index" }}" title="首页">首页</a>
<a href="{{urlfor "BlogController.List" }}" title="文章">文章</a>
</div>
<div class="btn-group dropdown-menu-right pull-right slidebar visible-xs-inline-block visible-sm-inline-block">
<button class="btn btn-default dropdown-toggle hidden-lg" type="button" data-toggle="dropdown"><i class="fa fa-align-justify"></i></button>