diff --git a/build/doc/db.sql b/build/doc/db.sql index 2a16eda..e63c72f 100644 --- a/build/doc/db.sql +++ b/build/doc/db.sql @@ -1,109 +1,133 @@ -CREATE TABLE `tank20_download_token` ( - `uuid` char(36) NOT NULL, - `user_uuid` char(36) DEFAULT NULL COMMENT '用户uuid', - `matter_uuid` char(36) DEFAULT NULL COMMENT '文件标识', +CREATE TABLE `tank20_download_token` +( + `uuid` char(36) NOT NULL, + `user_uuid` char(36) DEFAULT NULL COMMENT '用户uuid', + `matter_uuid` char(36) DEFAULT NULL COMMENT '文件标识', `expire_time` timestamp NULL DEFAULT NULL COMMENT '授权访问的次数', - `ip` varchar(45) DEFAULT NULL COMMENT '消费者的ip', - `sort` bigint(20) DEFAULT NULL, + `ip` varchar(45) DEFAULT NULL COMMENT '消费者的ip', + `sort` bigint(20) DEFAULT NULL, `update_time` timestamp NULL DEFAULT NULL, `create_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', PRIMARY KEY (`uuid`), UNIQUE KEY `id_UNIQUE` (`uuid`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='下载的token表'; -CREATE TABLE `tank20_image_cache` ( - `uuid` char(36) NOT NULL, - `user_uuid` char(36) DEFAULT NULL COMMENT '上传的用户id', - `matter_uuid` char(36) DEFAULT NULL, - `mode` varchar(512) DEFAULT NULL COMMENT '请求的uri', - `md5` varchar(45) DEFAULT NULL COMMENT '文件的md5值', - `size` bigint(20) DEFAULT '0' COMMENT '文件大小', - `path` varchar(255) DEFAULT NULL, - `sort` bigint(20) DEFAULT NULL, +CREATE TABLE `tank20_image_cache` +( + `uuid` char(36) NOT NULL, + `user_uuid` char(36) DEFAULT NULL COMMENT '上传的用户id', + `matter_uuid` char(36) DEFAULT NULL, + `mode` varchar(512) DEFAULT NULL COMMENT '请求的uri', + `md5` varchar(45) DEFAULT NULL COMMENT '文件的md5值', + `size` bigint(20) DEFAULT '0' COMMENT '文件大小', + `path` varchar(255) DEFAULT NULL, + `sort` bigint(20) DEFAULT NULL, `update_time` timestamp NULL DEFAULT NULL, `create_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', PRIMARY KEY (`uuid`), UNIQUE KEY `id_UNIQUE` (`uuid`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='图片缓存表'; -CREATE TABLE `tank20_matter` ( - `uuid` char(36) NOT NULL, - `puuid` varchar(45) DEFAULT NULL COMMENT '上一级的uuid', - `user_uuid` char(36) DEFAULT NULL COMMENT '上传的用户id', - `dir` tinyint(1) DEFAULT '0' COMMENT '是否是文件夹', - `alien` tinyint(1) DEFAULT '0', - `name` varchar(255) DEFAULT NULL COMMENT '文件名称', - `md5` varchar(45) DEFAULT NULL COMMENT '文件的md5值', - `size` bigint(20) DEFAULT '0' COMMENT '文件大小', - `privacy` tinyint(1) DEFAULT '0' COMMENT '文件是否是公有的', - `path` varchar(255) DEFAULT NULL, - `sort` bigint(20) DEFAULT NULL, +CREATE TABLE `tank20_matter` +( + `uuid` char(36) NOT NULL, + `puuid` varchar(45) DEFAULT NULL COMMENT '上一级的uuid', + `user_uuid` char(36) DEFAULT NULL COMMENT '上传的用户id', + `dir` tinyint(1) DEFAULT '0' COMMENT '是否是文件夹', + `alien` tinyint(1) DEFAULT '0', + `name` varchar(255) DEFAULT NULL COMMENT '文件名称', + `md5` varchar(45) DEFAULT NULL COMMENT '文件的md5值', + `size` bigint(20) DEFAULT '0' COMMENT '文件大小', + `privacy` tinyint(1) DEFAULT '0' COMMENT '文件是否是公有的', + `path` varchar(255) DEFAULT NULL, + `sort` bigint(20) DEFAULT NULL, `update_time` timestamp NULL DEFAULT NULL, `create_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', PRIMARY KEY (`uuid`), UNIQUE KEY `id_UNIQUE` (`uuid`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='file表'; -CREATE TABLE `tank20_preference` ( - `uuid` char(36) NOT NULL, - `name` varchar(45) DEFAULT NULL COMMENT '网站名称', - `logo_url` varchar(255) DEFAULT NULL, - `favicon_url` varchar(255) DEFAULT NULL, +CREATE TABLE `tank20_preference` +( + `uuid` char(36) NOT NULL, + `name` varchar(45) DEFAULT NULL COMMENT '网站名称', + `logo_url` varchar(255) DEFAULT NULL, + `favicon_url` varchar(255) DEFAULT NULL, `footer_line1` varchar(1024) DEFAULT NULL, `footer_line2` varchar(1024) DEFAULT NULL, - `version` varchar(45) DEFAULT NULL, - `sort` bigint(20) DEFAULT NULL, - `update_time` timestamp NULL DEFAULT NULL, - `create_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `version` varchar(45) DEFAULT NULL, + `sort` bigint(20) DEFAULT NULL, + `update_time` timestamp NULL DEFAULT NULL, + `create_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', PRIMARY KEY (`uuid`), UNIQUE KEY `id_UNIQUE` (`uuid`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='网站偏好设置表'; -CREATE TABLE `tank20_session` ( - `uuid` char(36) NOT NULL, - `authentication` char(36) DEFAULT NULL COMMENT '认证身份,存放在cookie中', - `user_uuid` char(36) DEFAULT NULL COMMENT '用户uuid', - `ip` varchar(45) DEFAULT NULL COMMENT '用户的ip地址', - `expire_time` timestamp NULL DEFAULT NULL, - `sort` bigint(20) DEFAULT NULL, +CREATE TABLE `tank20_security_visit` +( + `uuid` char(36) NOT NULL, + `session_id` varchar(45) DEFAULT NULL, + `user_uuid` char(36) DEFAULT NULL, + `ip` varchar(45) DEFAULT NULL, + `host` varchar(45) DEFAULT NULL, + `uri` varchar(255) DEFAULT NULL, + `params` text, + `cost` int(11) DEFAULT '0' COMMENT '耗时 ms', + `success` tinyint(1) DEFAULT '1', + `sort` bigint(20) NOT NULL DEFAULT '0', `update_time` timestamp NULL DEFAULT NULL, - `create_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (`uuid`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='访问记录表'; + +CREATE TABLE `tank20_session` +( + `uuid` char(36) NOT NULL, + `authentication` char(36) DEFAULT NULL COMMENT '认证身份,存放在cookie中', + `user_uuid` char(36) DEFAULT NULL COMMENT '用户uuid', + `ip` varchar(45) DEFAULT NULL COMMENT '用户的ip地址', + `expire_time` timestamp NULL DEFAULT NULL, + `sort` bigint(20) DEFAULT NULL, + `update_time` timestamp NULL DEFAULT NULL, + `create_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', PRIMARY KEY (`uuid`), UNIQUE KEY `id_UNIQUE` (`uuid`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='session表'; -CREATE TABLE `tank20_upload_token` ( - `uuid` char(36) NOT NULL, - `user_uuid` char(36) DEFAULT NULL COMMENT '用户uuid', - `folder_uuid` char(36) DEFAULT NULL, - `matter_uuid` char(36) DEFAULT NULL, - `filename` varchar(255) DEFAULT NULL COMMENT '文件后缀名的过滤,可以只允许用户上传特定格式的文件。', - `privacy` tinyint(1) DEFAULT '1', - `size` bigint(20) DEFAULT '0', +CREATE TABLE `tank20_upload_token` +( + `uuid` char(36) NOT NULL, + `user_uuid` char(36) DEFAULT NULL COMMENT '用户uuid', + `folder_uuid` char(36) DEFAULT NULL, + `matter_uuid` char(36) DEFAULT NULL, + `filename` varchar(255) DEFAULT NULL COMMENT '文件后缀名的过滤,可以只允许用户上传特定格式的文件。', + `privacy` tinyint(1) DEFAULT '1', + `size` bigint(20) DEFAULT '0', `expire_time` timestamp NULL DEFAULT NULL, - `ip` varchar(45) DEFAULT NULL COMMENT '消费者的ip', - `sort` bigint(20) DEFAULT NULL, + `ip` varchar(45) DEFAULT NULL COMMENT '消费者的ip', + `sort` bigint(20) DEFAULT NULL, `update_time` timestamp NULL DEFAULT NULL, `create_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', PRIMARY KEY (`uuid`), UNIQUE KEY `id_UNIQUE` (`uuid`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='上传的token表'; -CREATE TABLE `tank20_user` ( - `uuid` char(36) NOT NULL, - `role` varchar(45) DEFAULT 'USER', - `username` varchar(255) DEFAULT NULL COMMENT '昵称', - `password` varchar(255) DEFAULT NULL COMMENT '密码', - `email` varchar(45) DEFAULT NULL COMMENT '邮箱', - `phone` varchar(45) DEFAULT NULL COMMENT '电话', - `gender` varchar(45) DEFAULT 'UNKNOWN' COMMENT '性别,默认未知', - `city` varchar(45) DEFAULT NULL COMMENT '城市', - `avatar_url` varchar(255) DEFAULT NULL COMMENT '头像链接', - `last_time` datetime DEFAULT NULL COMMENT '上次登录使劲按', - `last_ip` varchar(45) DEFAULT NULL, - `size_limit` int(11) DEFAULT '-1' COMMENT '该账号上传文件的大小限制,单位byte。<0 表示不设限制', - `status` varchar(45) DEFAULT 'OK', - `sort` bigint(20) DEFAULT NULL, +CREATE TABLE `tank20_user` +( + `uuid` char(36) NOT NULL, + `role` varchar(45) DEFAULT 'USER', + `username` varchar(255) DEFAULT NULL COMMENT '昵称', + `password` varchar(255) DEFAULT NULL COMMENT '密码', + `email` varchar(45) DEFAULT NULL COMMENT '邮箱', + `phone` varchar(45) DEFAULT NULL COMMENT '电话', + `gender` varchar(45) DEFAULT 'UNKNOWN' COMMENT '性别,默认未知', + `city` varchar(45) DEFAULT NULL COMMENT '城市', + `avatar_url` varchar(255) DEFAULT NULL COMMENT '头像链接', + `last_time` datetime DEFAULT NULL COMMENT '上次登录使劲按', + `last_ip` varchar(45) DEFAULT NULL, + `size_limit` int(11) DEFAULT '-1' COMMENT '该账号上传文件的大小限制,单位byte。<0 表示不设限制', + `status` varchar(45) DEFAULT 'OK', + `sort` bigint(20) DEFAULT NULL, `update_time` timestamp NULL DEFAULT NULL, `create_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', PRIMARY KEY (`uuid`), diff --git a/rest/context.go b/rest/context.go index 1f439e8..8f34521 100644 --- a/rest/context.go +++ b/rest/context.go @@ -115,6 +115,11 @@ func (this *Context) registerBeans() { this.registerBean(new(PreferenceDao)) this.registerBean(new(PreferenceService)) + //securityVisit + this.registerBean(new(SecurityVisitController)) + this.registerBean(new(SecurityVisitDao)) + this.registerBean(new(SecurityVisitService)) + //session this.registerBean(new(SessionDao)) diff --git a/rest/router.go b/rest/router.go index fe9265f..cbe0025 100644 --- a/rest/router.go +++ b/rest/router.go @@ -1,6 +1,7 @@ package rest import ( + "encoding/json" "fmt" "github.com/json-iterator/go" "io" @@ -76,6 +77,58 @@ func (this *Router) GlobalPanicHandler(writer http.ResponseWriter, request *http } } +//记录访问记录 +func (this *Router) logSecurityVisit(writer http.ResponseWriter, request *http.Request) { + //手动装填本实例的Bean. 这里必须要用中间变量方可。 + var securityVisitDao *SecurityVisitDao + b := this.context.GetBean(securityVisitDao) + if b, ok := b.(*SecurityVisitDao); ok { + securityVisitDao = b + } + + fmt.Printf("Host = %s Uri = %s Path = %s RawPath = %s RawQuery = %s \n", + request.Host, + request.RequestURI, + request.URL.Path, + request.URL.RawPath, + request.URL.RawQuery) + + params := make(map[string][]string) + + //POST请求参数 + values := request.PostForm + for key, val := range values { + params[key] = val + } + //GET请求参数 + values1 := request.URL.Query() + for key, val := range values1 { + params[key] = val + } + + //用json的方式输出返回值。 + paramsString := "{}" + paramsData, err := json.Marshal(params) + if err == nil { + paramsString = string(paramsData) + } + + //将文件信息存入数据库中。 + securityVisit := &SecurityVisit{ + SessionId: "", + UserUuid: "testUserUUid", + Ip: GetIpAddress(request), + Host: request.Host, + Uri: request.URL.Path, + Params: paramsString, + Cost: 0, + Success: true, + } + + securityVisit = securityVisitDao.Create(securityVisit) + +} + //让Router具有处理请求的功能。 func (this *Router) ServeHTTP(writer http.ResponseWriter, request *http.Request) { @@ -86,6 +139,7 @@ func (this *Router) ServeHTTP(writer http.ResponseWriter, request *http.Request) path := request.URL.Path if strings.HasPrefix(path, "/api") { + if handler, ok := this.routeMap[path]; ok { handler(writer, request) @@ -108,6 +162,9 @@ func (this *Router) ServeHTTP(writer http.ResponseWriter, request *http.Request) } + //正常的访问记录会落到这里。 + go this.logSecurityVisit(writer, request) + } else { //当作静态资源处理。默认从当前文件下面的static文件夹中取东西。 dir := GetHtmlPath() @@ -141,4 +198,5 @@ func (this *Router) ServeHTTP(writer http.ResponseWriter, request *http.Request) } + } diff --git a/rest/security_visit_controller.go b/rest/security_visit_controller.go new file mode 100644 index 0000000..06244e0 --- /dev/null +++ b/rest/security_visit_controller.go @@ -0,0 +1,129 @@ +package rest + +import ( + "net/http" + "strconv" +) + +type SecurityVisitController struct { + BaseController + securityVisitDao *SecurityVisitDao + securityVisitService *SecurityVisitService +} + +//初始化方法 +func (this *SecurityVisitController) Init(context *Context) { + this.BaseController.Init(context) + + //手动装填本实例的Bean. 这里必须要用中间变量方可。 + b := context.GetBean(this.securityVisitDao) + if b, ok := b.(*SecurityVisitDao); ok { + this.securityVisitDao = b + } + + b = context.GetBean(this.securityVisitService) + if b, ok := b.(*SecurityVisitService); ok { + this.securityVisitService = b + } + +} + +//注册自己的路由。 +func (this *SecurityVisitController) RegisterRoutes() map[string]func(writer http.ResponseWriter, request *http.Request) { + + routeMap := make(map[string]func(writer http.ResponseWriter, request *http.Request)) + + //每个Controller需要主动注册自己的路由。 + routeMap["/api/security/visit/delete"] = this.Wrap(this.Delete, USER_ROLE_USER) + routeMap["/api/security/visit/detail"] = this.Wrap(this.Detail, USER_ROLE_USER) + routeMap["/api/security/visit/page"] = this.Wrap(this.Page, USER_ROLE_USER) + + return routeMap +} + +//查看详情。 +func (this *SecurityVisitController) Detail(writer http.ResponseWriter, request *http.Request) *WebResult { + + uuid := request.FormValue("uuid") + if uuid == "" { + return this.Error("图片缓存的uuid必填") + } + + securityVisit := this.securityVisitService.Detail(uuid) + + //验证当前之人是否有权限查看这么详细。 + user := this.checkUser(writer, request) + if user.Role != USER_ROLE_ADMINISTRATOR { + if securityVisit.UserUuid != user.Uuid { + panic("没有权限查看该图片缓存") + } + } + + return this.Success(securityVisit) + +} + +//按照分页的方式查询 +func (this *SecurityVisitController) Page(writer http.ResponseWriter, request *http.Request) *WebResult { + + //如果是根目录,那么就传入root. + pageStr := request.FormValue("page") + pageSizeStr := request.FormValue("pageSize") + userUuid := request.FormValue("userUuid") + orderCreateTime := request.FormValue("orderCreateTime") + orderSize := request.FormValue("orderSize") + + user := this.checkUser(writer, request) + if user.Role != USER_ROLE_ADMINISTRATOR { + userUuid = user.Uuid + } + + var page int + if pageStr != "" { + page, _ = strconv.Atoi(pageStr) + } + + pageSize := 200 + if pageSizeStr != "" { + tmp, err := strconv.Atoi(pageSizeStr) + if err == nil { + pageSize = tmp + } + } + + sortArray := []OrderPair{ + { + key: "create_time", + value: orderCreateTime, + }, + { + key: "size", + value: orderSize, + }, + } + + pager := this.securityVisitDao.Page(page, pageSize, userUuid, sortArray) + + return this.Success(pager) +} + +//删除一条记录 +func (this *SecurityVisitController) Delete(writer http.ResponseWriter, request *http.Request) *WebResult { + + uuid := request.FormValue("uuid") + if uuid == "" { + return this.Error("图片缓存的uuid必填") + } + + securityVisit := this.securityVisitDao.FindByUuid(uuid) + + //判断图片缓存的所属人是否正确 + user := this.checkUser(writer, request) + if user.Role != USER_ROLE_ADMINISTRATOR && securityVisit.UserUuid != user.Uuid { + return this.Error(CODE_WRAPPER_UNAUTHORIZED) + } + + this.securityVisitDao.Delete(securityVisit) + + return this.Success("删除成功!") +} diff --git a/rest/security_visit_dao.go b/rest/security_visit_dao.go new file mode 100644 index 0000000..4700ae7 --- /dev/null +++ b/rest/security_visit_dao.go @@ -0,0 +1,90 @@ +package rest + +import ( + "github.com/jinzhu/gorm" + _ "github.com/jinzhu/gorm/dialects/mysql" + "github.com/nu7hatch/gouuid" + "time" +) + +type SecurityVisitDao struct { + BaseDao +} + +//按照Id查询文件 +func (this *SecurityVisitDao) FindByUuid(uuid string) *SecurityVisit { + + // Read + var securityVisit SecurityVisit + db := this.context.DB.Where(&SecurityVisit{Base: Base{Uuid: uuid}}).First(&securityVisit) + if db.Error != nil { + return nil + } + return &securityVisit +} + +//按照Id查询文件 +func (this *SecurityVisitDao) CheckByUuid(uuid string) *SecurityVisit { + + // Read + var securityVisit SecurityVisit + db := this.context.DB.Where(&SecurityVisit{Base: Base{Uuid: uuid}}).First(&securityVisit) + this.PanicError(db.Error) + + return &securityVisit + +} + +//按分页条件获取分页 +func (this *SecurityVisitDao) Page(page int, pageSize int, userUuid string, sortArray []OrderPair) *Pager { + + var wp = &WherePair{} + + if userUuid != "" { + wp = wp.And(&WherePair{Query: "user_uuid = ?", Args: []interface{}{userUuid}}) + } + + var conditionDB *gorm.DB + conditionDB = this.context.DB.Model(&SecurityVisit{}).Where(wp.Query, wp.Args...) + + count := 0 + db := conditionDB.Count(&count) + this.PanicError(db.Error) + + var securityVisits []*SecurityVisit + db = conditionDB.Order(this.GetSortString(sortArray)).Offset(page * pageSize).Limit(pageSize).Find(&securityVisits) + this.PanicError(db.Error) + pager := NewPager(page, pageSize, count, securityVisits) + + return pager +} + +//创建 +func (this *SecurityVisitDao) Create(securityVisit *SecurityVisit) *SecurityVisit { + + timeUUID, _ := uuid.NewV4() + securityVisit.Uuid = string(timeUUID.String()) + securityVisit.CreateTime = time.Now() + securityVisit.UpdateTime = time.Now() + db := this.context.DB.Create(securityVisit) + this.PanicError(db.Error) + + return securityVisit +} + +//修改一条记录 +func (this *SecurityVisitDao) Save(securityVisit *SecurityVisit) *SecurityVisit { + + securityVisit.UpdateTime = time.Now() + db := this.context.DB.Save(securityVisit) + this.PanicError(db.Error) + + return securityVisit +} + +//删除一条记录 +func (this *SecurityVisitDao) Delete(securityVisit *SecurityVisit) { + + db := this.context.DB.Delete(&securityVisit) + this.PanicError(db.Error) +} diff --git a/rest/security_visit_model.go b/rest/security_visit_model.go new file mode 100644 index 0000000..8e62ba4 --- /dev/null +++ b/rest/security_visit_model.go @@ -0,0 +1,21 @@ +package rest + +/** + * 系统的所有访问记录均记录在此 + */ +type SecurityVisit struct { + Base + SessionId string `json:"sessionId"` + UserUuid string `json:"userUuid"` + Ip string `json:"ip"` + Host string `json:"host"` + Uri string `json:"uri"` + Params string `json:"params"` + Cost int64 `json:"cost"` + Success bool `json:"success"` +} + +// set File's table name to be `profiles` +func (SecurityVisit) TableName() string { + return TABLE_PREFIX + "security_visit" +} diff --git a/rest/security_visit_service.go b/rest/security_visit_service.go new file mode 100644 index 0000000..c2db25b --- /dev/null +++ b/rest/security_visit_service.go @@ -0,0 +1,32 @@ +package rest + +//@Service +type SecurityVisitService struct { + Bean + securityVisitDao *SecurityVisitDao + userDao *UserDao +} + +//初始化方法 +func (this *SecurityVisitService) Init(context *Context) { + + //手动装填本实例的Bean. 这里必须要用中间变量方可。 + b := context.GetBean(this.securityVisitDao) + if b, ok := b.(*SecurityVisitDao); ok { + this.securityVisitDao = b + } + + b = context.GetBean(this.userDao) + if b, ok := b.(*UserDao); ok { + this.userDao = b + } + +} + +//获取某个文件的详情,会把父级依次倒着装进去。如果中途出错,直接抛出异常。 +func (this *SecurityVisitService) Detail(uuid string) *SecurityVisit { + + securityVisit := this.securityVisitDao.CheckByUuid(uuid) + + return securityVisit +} diff --git a/rest/util_network.go b/rest/util_network.go index 5e22bc6..4bb9e3c 100644 --- a/rest/util_network.go +++ b/rest/util_network.go @@ -24,3 +24,10 @@ func GetIpAddress(r *http.Request) string { } return ipAddress } + +//根据一个请求,获取host +func GetHostFromRequest(r *http.Request) string { + + return r.Host + +} \ No newline at end of file