1
0
mirror of https://github.com/astaxie/beego.git synced 2025-07-11 12:11:02 +00:00

58 Commits

Author SHA1 Message Date
0692f92890 Merge branch 'develop' of https://github.com/astaxie/beego into develop 2014-09-02 14:42:02 +08:00
41adcf9966 change the version 1.4.0 to 1.4.1 2014-09-02 14:41:14 +08:00
e6b42a4070 Merge pull request #787 from francoishill/patch-21
Proposal for adding a ":" for stack trace printout
2014-08-31 08:52:32 +08:00
bc4780091b Proposal for adding a ":" for stack trace printout
Mainly useful for Sublime users with goto anything.
2014-08-30 21:27:21 +02:00
d8614e80e7 beego: update the Url to Path 2014-08-30 22:22:23 +08:00
c83a2a0925 modify the comments 2014-08-28 10:21:32 +08:00
50a21d60c1 apiauth add more comments & improve 2014-08-28 10:05:02 +08:00
5a087b28d2 aws api auth plugins 2014-08-28 00:25:50 +08:00
9b40271878 fix the less ` 2014-08-27 15:53:08 +08:00
770dc702f0 Merge pull request #782 from francoishill/patch-19
Extra field if username is empty
2014-08-27 15:36:07 +08:00
e146100a23 Merge pull request #783 from francoishill/patch-20
Typo in the printout of some level's prefixes
2014-08-27 15:35:31 +08:00
2d94f7797b Type in the printout of each level's prefix 2014-08-26 14:03:03 +02:00
61ce608847 Update smtp.go 2014-08-26 12:47:05 +02:00
2fe559701c fix the import 2014-08-26 15:43:18 +08:00
37fe175c26 Merge pull request #776 from devYu/master
udpate ini.go
2014-08-26 14:43:20 +08:00
fcc9d8c45f Merge pull request #778 from francoishill/patch-15
Update file.go
2014-08-26 14:42:52 +08:00
e51a9d6481 Merge pull request #779 from francoishill/patch-17
Print error if occurred in WriteMsg
2014-08-26 14:42:05 +08:00
e9a1daa3ee Merge pull request #780 from francoishill/patch-18
Allow mail with self-signed certificates
2014-08-26 14:41:36 +08:00
44ea260db1 Allow mail with self-signed certificates
For more information, refer to https://groups.google.com/forum/#!topic/golang-nuts/c9zEiH6ixyw
2014-08-26 06:52:18 +02:00
1d1ad69954 Print error if occurred in WriteMsg 2014-08-26 06:37:35 +02:00
59773dfabe Update file.go
New pull request for "Panic sometimes occurs at time 00h00 on windows, then the app crashes."
2014-08-26 06:25:59 +02:00
ccb61f0416 gofmt
Signed-off-by: devYu <devysq@gmail.com>
2014-08-25 21:31:53 +08:00
14629c214b 优化GetData
1. 去掉重复的ToLower
2. getData内部统一ToLower
3.调整getData中对空字符串判断位置
====
4. 待确定:在getData中是否有必要进行lock操作
2014-08-25 20:14:14 +08:00
a3888cef7f fix the comments for the \d 2014-08-25 19:48:02 +08:00
f6a1a6c9bf Merge pull request #773 from lei-cao/develop
Added data table admin ui
2014-08-25 07:32:07 +08:00
38ee43701d remove http: 2014-08-25 01:40:23 +08:00
421b796f1a Added data table admin ui 2014-08-25 01:37:11 +08:00
771179a3c6 Merge pull request #769 from JessonChan/develop
improve and typo fixed
2014-08-23 09:46:43 +08:00
c07b1d881b typo fixed 2014-08-23 07:07:12 +08:00
d8f2b05e08 improve the code and delete NO NEED URL CHECK 2014-08-23 07:02:47 +08:00
7b2fe824d5 typo fixed 2014-08-23 06:48:40 +08:00
02301caac1 modify desc 2014-08-23 06:47:42 +08:00
98c2307763 Merge pull request #767 from JessonChan/develop
bug fixed
2014-08-22 22:02:40 +08:00
485c2e865c bug fixed 2014-08-22 17:43:05 +08:00
b390667374 Merge remote-tracking branch 'upstream/develop' into develop
Conflicts:
	httplib/httplib.go
2014-08-22 17:39:57 +08:00
f684de2385 fixed bug-request dump too early 2014-08-22 17:12:46 +08:00
35b4022ee0 httplib: set the default proto 2014-08-22 17:03:56 +08:00
77294a5881 utils: fix the SliceIntersect 2014-08-22 16:50:07 +08:00
7b39bd7042 refactor 2014-08-22 16:43:42 +08:00
01e4084587 toolbox: fix the go routine asleep 2014-08-22 13:56:36 +08:00
bf429a3a20 Merge pull request #762 from pdf/pointer_field_support
Add support for basic type pointer fields
2014-08-22 13:41:58 +08:00
f8ff79d77d Merge pull request #764 from smallfish/master
Update README
2014-08-22 13:41:01 +08:00
e9487d3571 Update README 2014-08-22 13:32:54 +08:00
d7c3727f96 Add support for basic type pointer fields
Allows models like:

```
type User struct {
	Id    int64
	Name  string
	Email *string `orm:"null"`
}
```

This helps a lot when doing JSON marshalling/unmarshalling.

Pointer fields should always be declared with the NULL orm tag for sanity, this
probably requires documentation.
2014-08-22 14:25:32 +10:00
03eb1fc104 toolbox: add notify when add & delete task 2014-08-21 15:56:41 +08:00
0a967875da Merge pull request #757 from smallfish/develop
Add new function SetBasicAuth, and update README
2014-08-21 09:01:45 +08:00
6ae8bc1a16 Update README 2014-08-21 00:08:08 +08:00
e70537f8b3 Update README 2014-08-21 00:03:03 +08:00
9a583323a8 Add SetBasicAuth function for HTTP Auth 2014-08-20 23:36:58 +08:00
ff9c8d94e6 Merge pull request #753 from smallfish/develop
format comments
2014-08-20 16:40:26 +08:00
7f977a0c8c beego: change the colour 2014-08-20 11:54:25 +08:00
aaabeff44f change the route info 2014-08-20 10:59:38 +08:00
f85ac088c3 format comments 2014-08-19 16:48:30 +08:00
26da23266a Merge branch 'master' into develop 2014-08-19 09:49:07 +08:00
80274684e0 context: redirect should writer to response instantly 2014-08-19 09:48:21 +08:00
be005f9774 Merge pull request #750 from smallfish/develop
Update httplib support read data from response buffer, add some testcase...
2014-08-18 21:33:02 +08:00
c16b7be9ac rollback the ToFile func implement, and add testcase 2014-08-18 21:29:45 +08:00
de87529387 Update httplib support read data from response buffer, add some testcases 2014-08-18 21:01:49 +08:00
25 changed files with 928 additions and 306 deletions

View File

@ -1,32 +1,37 @@
## beego
## Beego
[![Build Status](https://drone.io/github.com/astaxie/beego/status.png)](https://drone.io/github.com/astaxie/beego/latest)
[![GoDoc](https://godoc.org/github.com/astaxie/beego?status.svg)](https://godoc.org/github.com/astaxie/beego)
beego is an open-source, high-performance, modularity, full-stack web framework.
More info [beego.me](http://beego.me)
## Installation
go get github.com/astaxie/beego
## Features
* RESTful support
* MVC architecture
* modularity
* auto API documents
* annotation router
* namespace
* powerful develop tools
* full stack for web & API
* Modularity
* Auto API documents
* Annotation router
* Namespace
* Powerful develop tools
* Full stack for Web & API
## Documentation
[English](http://beego.me/docs/intro/)
* [English](http://beego.me/docs/intro/)
* [中文文档](http://beego.me/docs/intro/)
[API](http://godoc.org/github.com/astaxie/beego)
[中文文档](http://beego.me/docs/intro/)
## Community
* [http://beego.me/community](http://beego.me/community)
## LICENSE
beego is licensed under the Apache Licence, Version 2.0
(http://www.apache.org/licenses/LICENSE-2.0.html).
(http://www.apache.org/licenses/LICENSE-2.0.html).

122
admin.go
View File

@ -142,121 +142,116 @@ func listConf(rw http.ResponseWriter, r *http.Request) {
tmpl.Execute(rw, data)
case "router":
resultList := new([][]string)
content := make(map[string]interface{})
var result = []string{
fmt.Sprintf("header"),
var fields = []string{
fmt.Sprintf("Router Pattern"),
fmt.Sprintf("Methods"),
fmt.Sprintf("Controller"),
}
*resultList = append(*resultList, result)
content["Fields"] = fields
methods := []string{}
methodsData := make(map[string]interface{})
for method, t := range BeeApp.Handlers.routers {
var result = []string{
fmt.Sprintf("success"),
fmt.Sprintf("Method: %s", method),
fmt.Sprintf(""),
fmt.Sprintf(""),
}
*resultList = append(*resultList, result)
resultList := new([][]string)
printTree(resultList, t)
methods = append(methods, method)
methodsData[method] = resultList
}
data["Content"] = resultList
content["Data"] = methodsData
content["Methods"] = methods
data["Content"] = content
data["Title"] = "Routers"
tmpl := template.Must(template.New("dashboard").Parse(dashboardTpl))
tmpl = template.Must(tmpl.Parse(routerAndFilterTpl))
tmpl = template.Must(tmpl.Parse(defaultScriptsTpl))
tmpl.Execute(rw, data)
case "filter":
resultList := new([][]string)
content := make(map[string]interface{})
var result = []string{
fmt.Sprintf("header"),
var fields = []string{
fmt.Sprintf("Router Pattern"),
fmt.Sprintf("Filter Function"),
}
*resultList = append(*resultList, result)
content["Fields"] = fields
filterTypes := []string{}
filterTypeData := make(map[string]interface{})
if BeeApp.Handlers.enableFilter {
var result = []string{
fmt.Sprintf("success"),
fmt.Sprintf("Before Router"),
fmt.Sprintf(""),
}
*resultList = append(*resultList, result)
var filterType string
if bf, ok := BeeApp.Handlers.filters[BeforeRouter]; ok {
filterType = "Before Router"
filterTypes = append(filterTypes, filterType)
resultList := new([][]string)
for _, f := range bf {
var result = []string{
fmt.Sprintf(""),
fmt.Sprintf("%s", f.pattern),
fmt.Sprintf("%s", utils.GetFuncName(f.filterFunc)),
}
*resultList = append(*resultList, result)
}
filterTypeData[filterType] = resultList
}
result = []string{
fmt.Sprintf("success"),
fmt.Sprintf("Before Exec"),
fmt.Sprintf(""),
}
*resultList = append(*resultList, result)
if bf, ok := BeeApp.Handlers.filters[BeforeExec]; ok {
filterType = "Before Exec"
filterTypes = append(filterTypes, filterType)
resultList := new([][]string)
for _, f := range bf {
var result = []string{
fmt.Sprintf(""),
fmt.Sprintf("%s", f.pattern),
fmt.Sprintf("%s", utils.GetFuncName(f.filterFunc)),
}
*resultList = append(*resultList, result)
}
filterTypeData[filterType] = resultList
}
result = []string{
fmt.Sprintf("success"),
fmt.Sprintf("AfterExec Exec"),
fmt.Sprintf(""),
}
*resultList = append(*resultList, result)
if bf, ok := BeeApp.Handlers.filters[AfterExec]; ok {
filterType = "After Exec"
filterTypes = append(filterTypes, filterType)
resultList := new([][]string)
for _, f := range bf {
var result = []string{
fmt.Sprintf(""),
fmt.Sprintf("%s", f.pattern),
fmt.Sprintf("%s", utils.GetFuncName(f.filterFunc)),
}
*resultList = append(*resultList, result)
}
filterTypeData[filterType] = resultList
}
result = []string{
fmt.Sprintf("success"),
fmt.Sprintf("Finish Router"),
fmt.Sprintf(""),
}
*resultList = append(*resultList, result)
if bf, ok := BeeApp.Handlers.filters[FinishRouter]; ok {
filterType = "Finish Router"
filterTypes = append(filterTypes, filterType)
resultList := new([][]string)
for _, f := range bf {
var result = []string{
fmt.Sprintf(""),
fmt.Sprintf("%s", f.pattern),
fmt.Sprintf("%s", utils.GetFuncName(f.filterFunc)),
}
*resultList = append(*resultList, result)
}
filterTypeData[filterType] = resultList
}
}
data["Content"] = resultList
content["Data"] = filterTypeData
content["Methods"] = filterTypes
data["Content"] = content
data["Title"] = "Filters"
tmpl := template.Must(template.New("dashboard").Parse(dashboardTpl))
tmpl = template.Must(tmpl.Parse(routerAndFilterTpl))
@ -281,7 +276,6 @@ func printTree(resultList *[][]string, t *Tree) {
if v, ok := l.runObject.(*controllerInfo); ok {
if v.routerType == routerTypeBeego {
var result = []string{
fmt.Sprintf(""),
fmt.Sprintf("%s", v.pattern),
fmt.Sprintf("%s", v.methods),
fmt.Sprintf("%s", v.controllerType),
@ -289,7 +283,6 @@ func printTree(resultList *[][]string, t *Tree) {
*resultList = append(*resultList, result)
} else if v.routerType == routerTypeRESTFul {
var result = []string{
fmt.Sprintf(""),
fmt.Sprintf("%s", v.pattern),
fmt.Sprintf("%s", v.methods),
fmt.Sprintf(""),
@ -297,7 +290,6 @@ func printTree(resultList *[][]string, t *Tree) {
*resultList = append(*resultList, result)
} else if v.routerType == routerTypeHandler {
var result = []string{
fmt.Sprintf(""),
fmt.Sprintf("%s", v.pattern),
fmt.Sprintf(""),
fmt.Sprintf(""),
@ -352,13 +344,15 @@ func profIndex(rw http.ResponseWriter, r *http.Request) {
func healthcheck(rw http.ResponseWriter, req *http.Request) {
data := make(map[interface{}]interface{})
resultList := new([][]string)
var result = []string{
fmt.Sprintf("header"),
var result = []string{}
fields := []string{
fmt.Sprintf("Name"),
fmt.Sprintf("Message"),
fmt.Sprintf("Status"),
}
*resultList = append(*resultList, result)
resultList := new([][]string)
content := make(map[string]interface{})
for name, h := range toolbox.AdminCheckList {
if err := h.Check(); err != nil {
@ -379,7 +373,9 @@ func healthcheck(rw http.ResponseWriter, req *http.Request) {
*resultList = append(*resultList, result)
}
data["Content"] = resultList
content["Fields"] = fields
content["Data"] = resultList
data["Content"] = content
data["Title"] = "Health Check"
tmpl := template.Must(template.New("dashboard").Parse(dashboardTpl))
tmpl = template.Must(tmpl.Parse(healthCheckTpl))
@ -410,17 +406,17 @@ func taskStatus(rw http.ResponseWriter, req *http.Request) {
}
// List Tasks
content := make(map[string]interface{})
resultList := new([][]string)
var result = []string{
fmt.Sprintf("header"),
var result = []string{}
var fields = []string{
fmt.Sprintf("Task Name"),
fmt.Sprintf("Task Spec"),
fmt.Sprintf("Task Function"),
fmt.Sprintf(""),
}
*resultList = append(*resultList, result)
for tname, tk := range toolbox.AdminTaskList {
result = []string{
fmt.Sprintf(""),
fmt.Sprintf("%s", tname),
fmt.Sprintf("%s", tk.GetStatus()),
fmt.Sprintf("%s", tk.GetPrev().String()),
@ -428,7 +424,9 @@ func taskStatus(rw http.ResponseWriter, req *http.Request) {
*resultList = append(*resultList, result)
}
data["Content"] = resultList
content["Fields"] = fields
content["Data"] = resultList
data["Content"] = content
data["Title"] = "Tasks"
tmpl := template.Must(template.New("dashboard").Parse(dashboardTpl))
tmpl = template.Must(tmpl.Parse(tasksTpl))

View File

@ -61,31 +61,35 @@ var gcAjaxTpl = `
{{end}}
`
var qpsTpl = `
{{define "content"}}
var qpsTpl = `{{define "content"}}
<h1>Requests statistics</h1>
<table class="table table-striped table-hover ">
{{range $i, $slice := .Content}}
<tr>
{{range $j, $elem := $slice}}
{{if eq $i 0}}
<th>
{{else}}
<td>
{{end}}
{{$elem}}
{{if eq $i 0}}
</th>
{{else}}
</td>
{{end}}
{{end}}
<thead>
<tr>
{{range .Content.Fields}}
<th>
{{.}}
</th>
{{end}}
</tr>
</thead>
<tbody>
{{range $i, $elem := .Content.Data}}
<tr>
{{range $elem}}
<td>
{{.}}
</td>
{{end}}
</tr>
{{end}}
</tbody>
</tr>
{{end}}
</table>
{{end}}
`
{{end}}`
var configTpl = `
{{define "content"}}
@ -98,49 +102,51 @@ var configTpl = `
{{end}}
`
var routerAndFilterTpl = `
{{define "content"}}
var routerAndFilterTpl = `{{define "content"}}
<h1>{{.Title}}</h1>
{{range .Content.Methods}}
<div class="panel panel-default">
<div class="panel-heading lead success"><strong>{{.}}</strong></div>
<div class="panel-body">
<table class="table table-striped table-hover ">
{{range $i, $slice := .Content}}
<tr>
<thead>
<tr>
{{range $.Content.Fields}}
<th>
{{.}}
</th>
{{end}}
</tr>
</thead>
{{ $header := index $slice 0}}
{{if eq "header" $header }}
{{range $j, $elem := $slice}}
{{if ne $j 0}}
<th>
{{$elem}}
</th>
{{end}}
{{end}}
{{else if eq "success" $header}}
{{range $j, $elem := $slice}}
{{if ne $j 0}}
<th class="success">
{{$elem}}
</th>
{{end}}
{{end}}
{{else}}
{{range $j, $elem := $slice}}
{{if ne $j 0}}
<td>
{{$elem}}
</td>
{{end}}
{{end}}
{{end}}
<tbody>
{{$slice := index $.Content.Data .}}
{{range $i, $elem := $slice}}
<tr>
{{range $elem}}
<td>
{{.}}
</td>
{{end}}
</tr>
{{end}}
</tbody>
</tr>
{{end}}
</table>
</div>
</div>
{{end}}
`
var tasksTpl = `
{{define "content"}}
{{end}}`
var tasksTpl = `{{define "content"}}
<h1>{{.Title}}</h1>
@ -161,59 +167,51 @@ bg-warning
<table class="table table-striped table-hover ">
{{range $i, $slice := .Content}}
<thead>
<tr>
{{range .Content.Fields}}
<th>
{{.}}
</th>
{{end}}
</tr>
</thead>
{{ $header := index $slice 0}}
{{if eq "header" $header }}
{{range $j, $elem := $slice}}
{{if ne $j 0}}
<th>
{{$elem}}
</th>
{{end}}
{{end}}
<th>
Run Task
</th>
{{else}}
{{range $j, $elem := $slice}}
{{if ne $j 0}}
<tbody>
{{range $i, $slice := .Content.Data}}
<tr>
{{range $slice}}
<td>
{{$elem}}
{{.}}
</td>
{{end}}
{{end}}
<td>
<a class="btn btn-primary btn-sm" href="/task?taskname={{index $slice 1}}">Run</a>
</td>
{{end}}
</tr>
{{end}}
</tbody>
</table>
{{end}}
`
{{end}}`
var healthCheckTpl = `
{{define "content"}}
<h1>{{.Title}}</h1>
<table class="table table-striped table-hover ">
{{range $i, $slice := .Content}}
{{ $header := index $slice 0}}
{{if eq "header" $header }}
<thead>
<tr>
{{range $j, $elem := $slice}}
{{if ne $j 0}}
{{range .Content.Fields}}
<th>
{{$elem}}
{{.}}
</th>
{{end}}
{{end}}
{{end}}
</tr>
{{else}}
</thead>
<tbody>
{{range $i, $slice := .Content.Data}}
{{ $header := index $slice 0}}
{{ if eq "success" $header}}
<tr class="success">
{{else if eq "error" $header}}
@ -228,10 +226,13 @@ var healthCheckTpl = `
</td>
{{end}}
{{end}}
<td>
{{$header}}
</td>
</tr>
{{end}}
{{end}}
</tbody>
</table>
{{end}}`
@ -251,7 +252,8 @@ Welcome to Beego Admin Dashboard
</title>
<link href="http://maxcdn.bootstrapcdn.com/bootstrap/3.2.0/css/bootstrap.min.css" rel="stylesheet">
<link href="//maxcdn.bootstrapcdn.com/bootstrap/3.2.0/css/bootstrap.min.css" rel="stylesheet">
<link href="//cdn.datatables.net/plug-ins/725b2a2115b/integration/bootstrap/3/dataTables.bootstrap.css" rel="stylesheet">
<style type="text/css">
ul.nav li.dropdown:hover > ul.dropdown-menu {
@ -336,9 +338,17 @@ Healthcheck
{{template "content" .}}
</div>
<script src="http://code.jquery.com/jquery-1.11.1.min.js"></script>
<script src="http://maxcdn.bootstrapcdn.com/bootstrap/3.2.0/js/bootstrap.min.js"></script>
<script src="//code.jquery.com/jquery-1.11.1.min.js"></script>
<script src="//maxcdn.bootstrapcdn.com/bootstrap/3.2.0/js/bootstrap.min.js"></script>
<script src="//cdn.datatables.net/1.10.2/js/jquery.dataTables.min.js"></script>
<script src="//cdn.datatables.net/plug-ins/725b2a2115b/integration/bootstrap/3/dataTables.bootstrap.js
"></script>
<script type="text/javascript">
$(document).ready(function() {
$('.table').dataTable();
});
</script>
{{template "scripts" .}}
</body>
</html>

View File

@ -14,13 +14,13 @@
// beego is an open-source, high-performance, modularity, full-stack web framework
//
// package main
// package main
//
// import "github.com/astaxie/beego"
// import "github.com/astaxie/beego"
//
// func main() {
// beego.Run()
// }
// func main() {
// beego.Run()
// }
//
// more infomation: http://beego.me
package beego
@ -38,7 +38,7 @@ import (
)
// beego web framework version.
const VERSION = "1.4.0"
const VERSION = "1.4.1"
type hookfunc func() error //hook function to run
var hooks []hookfunc //hook function slice to store the hookfunc

View File

@ -147,7 +147,7 @@ type IniConfigContainer struct {
// Bool returns the boolean value for a given key.
func (c *IniConfigContainer) Bool(key string) (bool, error) {
return strconv.ParseBool(c.getdata(strings.ToLower(key)))
return strconv.ParseBool(c.getdata(key))
}
// DefaultBool returns the boolean value for a given key.
@ -162,7 +162,7 @@ func (c *IniConfigContainer) DefaultBool(key string, defaultval bool) bool {
// Int returns the integer value for a given key.
func (c *IniConfigContainer) Int(key string) (int, error) {
return strconv.Atoi(c.getdata(strings.ToLower(key)))
return strconv.Atoi(c.getdata(key))
}
// DefaultInt returns the integer value for a given key.
@ -177,7 +177,7 @@ func (c *IniConfigContainer) DefaultInt(key string, defaultval int) int {
// Int64 returns the int64 value for a given key.
func (c *IniConfigContainer) Int64(key string) (int64, error) {
return strconv.ParseInt(c.getdata(strings.ToLower(key)), 10, 64)
return strconv.ParseInt(c.getdata(key), 10, 64)
}
// DefaultInt64 returns the int64 value for a given key.
@ -192,7 +192,7 @@ func (c *IniConfigContainer) DefaultInt64(key string, defaultval int64) int64 {
// Float returns the float value for a given key.
func (c *IniConfigContainer) Float(key string) (float64, error) {
return strconv.ParseFloat(c.getdata(strings.ToLower(key)), 64)
return strconv.ParseFloat(c.getdata(key), 64)
}
// DefaultFloat returns the float64 value for a given key.
@ -207,8 +207,7 @@ func (c *IniConfigContainer) DefaultFloat(key string, defaultval float64) float6
// String returns the string value for a given key.
func (c *IniConfigContainer) String(key string) string {
key = strings.ToLower(key)
return c.getdata(strings.ToLower(key))
return c.getdata(key)
}
// DefaultString returns the string value for a given key.
@ -338,15 +337,15 @@ func (c *IniConfigContainer) DIY(key string) (v interface{}, err error) {
// section.key or key
func (c *IniConfigContainer) getdata(key string) string {
c.RLock()
defer c.RUnlock()
if len(key) == 0 {
return ""
}
c.RLock()
defer c.RUnlock()
var (
section, k string
sectionKey []string = strings.Split(key, "::")
sectionKey []string = strings.Split(strings.ToLower(key), "::")
)
if len(sectionKey) >= 2 {
section = sectionKey[0]

View File

@ -49,7 +49,7 @@ type Context struct {
// It sends http response header directly.
func (ctx *Context) Redirect(status int, localurl string) {
ctx.Output.Header("Location", localurl)
ctx.Output.SetStatus(status)
ctx.ResponseWriter.WriteHeader(status)
}
// Abort stops this request.

View File

@ -60,7 +60,7 @@ func (input *BeegoInput) Uri() string {
// Url returns request url path (without query string, fragment).
func (input *BeegoInput) Url() string {
return input.Request.URL.String()
return input.Request.URL.Path
}
// Site returns base site url as scheme://domain type.

View File

@ -6,53 +6,71 @@ httplib is an libs help you to curl remote url.
## GET
you can use Get to crawl data.
import "httplib"
import "github.com/astaxie/beego/httplib"
str, err := httplib.Get("http://beego.me/").String()
if err != nil {
t.Fatal(err)
// error
}
fmt.Println(str)
## POST
POST data to remote url
b:=httplib.Post("http://beego.me/")
b.Param("username","astaxie")
b.Param("password","123456")
str, err := b.String()
req := httplib.Post("http://beego.me/")
req.Param("username","astaxie")
req.Param("password","123456")
str, err := req.String()
if err != nil {
t.Fatal(err)
// error
}
fmt.Println(str)
## set timeout
you can set timeout in request.default is 60 seconds.
## Set timeout
set Get timeout:
The default timeout is `60` seconds, function prototype:
SetTimeout(connectTimeout, readWriteTimeout time.Duration)
Exmaple:
// GET
httplib.Get("http://beego.me/").SetTimeout(100 * time.Second, 30 * time.Second)
set post timeout:
// POST
httplib.Post("http://beego.me/").SetTimeout(100 * time.Second, 30 * time.Second)
- first param is connectTimeout.
- second param is readWriteTimeout
## debug
if you want to debug the request info, set the debug on
## Debug
If you want to debug the request info, set the debug on
httplib.Get("http://beego.me/").Debug(true)
## support HTTPS client
if request url is https. You can set the client support TSL:
## Set HTTP Basic Auth
str, err := Get("http://beego.me/").SetBasicAuth("user", "passwd").String()
if err != nil {
// error
}
fmt.Println(str)
## Set HTTPS
If request url is https, You can set the client support TSL:
httplib.SetTLSClientConfig(&tls.Config{InsecureSkipVerify: true})
more info about the tls.Config please visit http://golang.org/pkg/crypto/tls/#Config
## set cookie
More info about the `tls.Config` please visit http://golang.org/pkg/crypto/tls/#Config
## Set HTTP Version
some servers need to specify the protocol version of HTTP
httplib.Get("http://beego.me/").SetProtocolVersion("HTTP/1.1")
## Set Cookie
some http request need setcookie. So set it like this:
cookie := &http.Cookie{}
@ -60,21 +78,20 @@ some http request need setcookie. So set it like this:
cookie.Value = "astaxie"
httplib.Get("http://beego.me/").SetCookie(cookie)
## upload file
httplib support mutil file upload, use `b.PostFile()`
## Upload file
b:=httplib.Post("http://beego.me/")
b.Param("username","astaxie")
b.Param("password","123456")
b.PostFile("uploadfile1", "httplib.pdf")
b.PostFile("uploadfile2", "httplib.txt")
str, err := b.String()
httplib support mutil file upload, use `req.PostFile()`
req := httplib.Post("http://beego.me/")
req.Param("username","astaxie")
req.PostFile("uploadfile1", "httplib.pdf")
str, err := req.String()
if err != nil {
t.Fatal(err)
// error
}
fmt.Println(str)
## set HTTP version
some servers need to specify the protocol version of HTTP
httplib.Get("http://beego.me/").SetProtocolVersion("HTTP/1.1")
See godoc for further documentation and examples.
* [godoc.org/github.com/astaxie/beego/httplib](https://godoc.org/github.com/astaxie/beego/httplib)

View File

@ -14,9 +14,9 @@
// Usage:
//
// import "github.com/astaxie/beego/context"
// import "github.com/astaxie/beego/httplib"
//
// b:=httplib.Post("http://beego.me/")
// b := httplib.Post("http://beego.me/")
// b.Param("username","astaxie")
// b.Param("password","123456")
// b.PostFile("uploadfile1", "httplib.pdf")
@ -53,7 +53,7 @@ var defaultSetting = BeegoHttpSettings{false, "beegoServer", 60 * time.Second, 6
var defaultCookieJar http.CookieJar
var settingMutex sync.Mutex
// createDefaultCookieJar creates a global cookiejar to store cookies.
// createDefaultCookie creates a global cookiejar to store cookies.
func createDefaultCookie() {
settingMutex.Lock()
defer settingMutex.Unlock()
@ -73,44 +73,42 @@ func SetDefaultSetting(setting BeegoHttpSettings) {
}
}
// return *BeegoHttpRequest with specific method
func newBeegoRequest(url, method string) *BeegoHttpRequest {
var resp http.Response
req := http.Request{
Method: method,
Header: make(http.Header),
Proto: "HTTP/1.1",
ProtoMajor: 1,
ProtoMinor: 1,
}
return &BeegoHttpRequest{url, &req, map[string]string{}, map[string]string{}, defaultSetting, &resp, nil}
}
// Get returns *BeegoHttpRequest with GET method.
func Get(url string) *BeegoHttpRequest {
var req http.Request
req.Method = "GET"
req.Header = http.Header{}
return &BeegoHttpRequest{url, &req, map[string]string{}, map[string]string{}, defaultSetting}
return newBeegoRequest(url, "GET")
}
// Post returns *BeegoHttpRequest with POST method.
func Post(url string) *BeegoHttpRequest {
var req http.Request
req.Method = "POST"
req.Header = http.Header{}
return &BeegoHttpRequest{url, &req, map[string]string{}, map[string]string{}, defaultSetting}
return newBeegoRequest(url, "POST")
}
// Put returns *BeegoHttpRequest with PUT method.
func Put(url string) *BeegoHttpRequest {
var req http.Request
req.Method = "PUT"
req.Header = http.Header{}
return &BeegoHttpRequest{url, &req, map[string]string{}, map[string]string{}, defaultSetting}
return newBeegoRequest(url, "PUT")
}
// Delete returns *BeegoHttpRequest DELETE GET method.
// Delete returns *BeegoHttpRequest DELETE method.
func Delete(url string) *BeegoHttpRequest {
var req http.Request
req.Method = "DELETE"
req.Header = http.Header{}
return &BeegoHttpRequest{url, &req, map[string]string{}, map[string]string{}, defaultSetting}
return newBeegoRequest(url, "DELETE")
}
// Head returns *BeegoHttpRequest with HEAD method.
func Head(url string) *BeegoHttpRequest {
var req http.Request
req.Method = "HEAD"
req.Header = http.Header{}
return &BeegoHttpRequest{url, &req, map[string]string{}, map[string]string{}, defaultSetting}
return newBeegoRequest(url, "HEAD")
}
// BeegoHttpSettings
@ -132,6 +130,8 @@ type BeegoHttpRequest struct {
params map[string]string
files map[string]string
setting BeegoHttpSettings
resp *http.Response
body []byte
}
// Change request settings
@ -140,6 +140,12 @@ func (b *BeegoHttpRequest) Setting(setting BeegoHttpSettings) *BeegoHttpRequest
return b
}
// SetBasicAuth sets the request's Authorization header to use HTTP Basic Authentication with the provided username and password.
func (b *BeegoHttpRequest) SetBasicAuth(username, password string) *BeegoHttpRequest {
b.req.SetBasicAuth(username, password)
return b
}
// SetEnableCookie sets enable/disable cookiejar
func (b *BeegoHttpRequest) SetEnableCookie(enable bool) *BeegoHttpRequest {
b.setting.EnableCookie = enable
@ -247,6 +253,9 @@ func (b *BeegoHttpRequest) Body(data interface{}) *BeegoHttpRequest {
}
func (b *BeegoHttpRequest) getResponse() (*http.Response, error) {
if b.resp.StatusCode != 0 {
return b.resp, nil
}
var paramBody string
if len(b.params) > 0 {
var buf bytes.Buffer
@ -301,22 +310,11 @@ func (b *BeegoHttpRequest) getResponse() (*http.Response, error) {
}
url, err := url.Parse(b.url)
if url.Scheme == "" {
b.url = "http://" + b.url
url, err = url.Parse(b.url)
}
if err != nil {
return nil, err
}
b.req.URL = url
if b.setting.ShowDebug {
dump, err := httputil.DumpRequest(b.req, true)
if err != nil {
println(err.Error())
}
println(string(dump))
}
trans := b.setting.Transport
@ -361,10 +359,19 @@ func (b *BeegoHttpRequest) getResponse() (*http.Response, error) {
b.req.Header.Set("User-Agent", b.setting.UserAgent)
}
if b.setting.ShowDebug {
dump, err := httputil.DumpRequest(b.req, true)
if err != nil {
println(err.Error())
}
println(string(dump))
}
resp, err := client.Do(b.req)
if err != nil {
return nil, err
}
b.resp = resp
return resp, nil
}
@ -382,6 +389,9 @@ func (b *BeegoHttpRequest) String() (string, error) {
// Bytes returns the body []byte in response.
// it calls Response inner.
func (b *BeegoHttpRequest) Bytes() ([]byte, error) {
if b.body != nil {
return b.body, nil
}
resp, err := b.getResponse()
if err != nil {
return nil, err
@ -394,6 +404,7 @@ func (b *BeegoHttpRequest) Bytes() ([]byte, error) {
if err != nil {
return nil, err
}
b.body = data
return data, nil
}

View File

@ -15,27 +15,51 @@
package httplib
import (
"io/ioutil"
"os"
"strings"
"testing"
)
func TestSimpleGet(t *testing.T) {
str, err := Get("http://httpbin.org/get").String()
func TestResponse(t *testing.T) {
req := Get("http://httpbin.org/get")
resp, err := req.Response()
if err != nil {
t.Fatal(err)
}
t.Log(str)
t.Log(resp)
}
func TestGet(t *testing.T) {
req := Get("http://httpbin.org/get")
b, err := req.Bytes()
if err != nil {
t.Fatal(err)
}
t.Log(b)
s, err := req.String()
if err != nil {
t.Fatal(err)
}
t.Log(s)
if string(b) != s {
t.Fatal("request data not match")
}
}
func TestSimplePost(t *testing.T) {
v := "smallfish"
req := Post("http://httpbin.org/post")
req.Param("username", v)
str, err := req.String()
if err != nil {
t.Fatal(err)
}
t.Log(str)
n := strings.Index(str, v)
if n == -1 {
t.Fatal(v + " not found in post")
@ -47,17 +71,35 @@ func TestPostFile(t *testing.T) {
req := Post("http://httpbin.org/post")
req.Param("username", v)
req.PostFile("uploadfile", "httplib_test.go")
str, err := req.String()
if err != nil {
t.Fatal(err)
}
t.Log(str)
n := strings.Index(str, v)
if n == -1 {
t.Fatal(v + " not found in post")
}
}
func TestSimplePut(t *testing.T) {
str, err := Put("http://httpbin.org/put").String()
if err != nil {
t.Fatal(err)
}
t.Log(str)
}
func TestSimpleDelete(t *testing.T) {
str, err := Delete("http://httpbin.org/delete").String()
if err != nil {
t.Fatal(err)
}
t.Log(str)
}
func TestWithCookie(t *testing.T) {
v := "smallfish"
str, err := Get("http://httpbin.org/cookies/set?k1=" + v).SetEnableCookie(true).String()
@ -65,17 +107,31 @@ func TestWithCookie(t *testing.T) {
t.Fatal(err)
}
t.Log(str)
str, err = Get("http://httpbin.org/cookies").SetEnableCookie(true).String()
if err != nil {
t.Fatal(err)
}
t.Log(str)
n := strings.Index(str, v)
if n == -1 {
t.Fatal(v + " not found in cookie")
}
}
func TestWithBasicAuth(t *testing.T) {
str, err := Get("http://httpbin.org/basic-auth/user/passwd").SetBasicAuth("user", "passwd").String()
if err != nil {
t.Fatal(err)
}
t.Log(str)
n := strings.Index(str, "authenticated")
if n == -1 {
t.Fatal("authenticated not found in response")
}
}
func TestWithUserAgent(t *testing.T) {
v := "beego"
str, err := Get("http://httpbin.org/headers").SetUserAgent(v).String()
@ -83,6 +139,7 @@ func TestWithUserAgent(t *testing.T) {
t.Fatal(err)
}
t.Log(str)
n := strings.Index(str, v)
if n == -1 {
t.Fatal(v + " not found in user-agent")
@ -102,8 +159,47 @@ func TestWithSetting(t *testing.T) {
t.Fatal(err)
}
t.Log(str)
n := strings.Index(str, v)
if n == -1 {
t.Fatal(v + " not found in user-agent")
}
}
func TestToJson(t *testing.T) {
req := Get("http://httpbin.org/ip")
resp, err := req.Response()
if err != nil {
t.Fatal(err)
}
t.Log(resp)
// httpbin will return http remote addr
type Ip struct {
Origin string `json:"origin"`
}
var ip Ip
err = req.ToJson(&ip)
if err != nil {
t.Fatal(err)
}
t.Log(ip.Origin)
if n := strings.Count(ip.Origin, "."); n != 3 {
t.Fatal("response is not valid ip")
}
}
func TestToFile(t *testing.T) {
f := "beego_testfile"
req := Get("http://httpbin.org/ip")
err := req.ToFile(f)
if err != nil {
t.Fatal(err)
}
defer os.Remove(f)
b, err := ioutil.ReadFile(f)
if n := strings.Index(string(b), "origin"); n == -1 {
t.Fatal(err)
}
}

View File

@ -38,8 +38,8 @@ var colors = []Brush{
NewBrush("1;31"), // Error red
NewBrush("1;33"), // Warning yellow
NewBrush("1;32"), // Notice green
NewBrush("1;34"), // Informational green
NewBrush("1;30"), // Debug black
NewBrush("1;34"), // Informational blue
NewBrush("1;34"), // Debug blue
}
// ConsoleWriter implements LoggerInterface and writes messages to terminal.

View File

@ -226,13 +226,20 @@ func (w *FileLogWriter) DoRotate() error {
func (w *FileLogWriter) deleteOldLog() {
dir := filepath.Dir(w.Filename)
filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {
filepath.Walk(dir, func(path string, info os.FileInfo, err error) (returnErr error) {
defer func() {
if r := recover(); r != nil {
returnErr = fmt.Errorf("Unable to delete old log '%s', error: %+v", path, r)
fmt.Println(returnErr)
}
}()
if !info.IsDir() && info.ModTime().Unix() < (time.Now().Unix()-60*60*24*w.Maxdays) {
if strings.HasPrefix(filepath.Base(path), filepath.Base(w.Filename)) {
os.Remove(path)
}
}
return nil
return
})
}

View File

@ -193,7 +193,10 @@ func (bl *BeeLogger) startLogger() {
select {
case bm := <-bl.msg:
for _, l := range bl.outputs {
l.WriteMsg(bm.msg, bm.level)
err := l.WriteMsg(bm.msg, bm.level)
if err != nil {
fmt.Println("ERROR, unable to WriteMsg:", err)
}
}
}
}
@ -201,13 +204,13 @@ func (bl *BeeLogger) startLogger() {
// Log EMERGENCY level message.
func (bl *BeeLogger) Emergency(format string, v ...interface{}) {
msg := fmt.Sprintf("[D] "+format, v...)
msg := fmt.Sprintf("[M] "+format, v...)
bl.writerMsg(LevelEmergency, msg)
}
// Log ALERT level message.
func (bl *BeeLogger) Alert(format string, v ...interface{}) {
msg := fmt.Sprintf("[D] "+format, v...)
msg := fmt.Sprintf("[A] "+format, v...)
bl.writerMsg(LevelAlert, msg)
}
@ -231,7 +234,7 @@ func (bl *BeeLogger) Warning(format string, v ...interface{}) {
// Log NOTICE level message.
func (bl *BeeLogger) Notice(format string, v ...interface{}) {
msg := fmt.Sprintf("[W] "+format, v...)
msg := fmt.Sprintf("[N] "+format, v...)
bl.writerMsg(LevelNotice, msg)
}
@ -281,7 +284,10 @@ func (bl *BeeLogger) Close() {
if len(bl.msg) > 0 {
bm := <-bl.msg
for _, l := range bl.outputs {
l.WriteMsg(bm.msg, bm.level)
err := l.WriteMsg(bm.msg, bm.level)
if err != nil {
fmt.Println("ERROR, unable to WriteMsg (while closing logger):", err)
}
}
} else {
break

View File

@ -15,8 +15,10 @@
package logs
import (
"crypto/tls"
"encoding/json"
"fmt"
"net"
"net/smtp"
"strings"
"time"
@ -32,6 +34,7 @@ type SmtpWriter struct {
Password string `json:"password"`
Host string `json:"Host"`
Subject string `json:"subject"`
FromAddress string `json:"fromAddress"`
RecipientAddresses []string `json:"sendTos"`
Level int `json:"level"`
}
@ -48,6 +51,7 @@ func NewSmtpWriter() LoggerInterface {
// "password:"password",
// "host":"smtp.gmail.com:465",
// "subject":"email title",
// "fromAddress":"from@example.com",
// "sendTos":["email1","email2"],
// "level":LevelError
// }
@ -71,6 +75,59 @@ func (s *SmtpWriter) GetSmtpAuth(host string) smtp.Auth {
)
}
func (s *SmtpWriter) sendMail(hostAddressWithPort string, auth smtp.Auth, fromAddress string, recipients []string, msgContent []byte) error {
client, err := smtp.Dial(hostAddressWithPort)
if err != nil {
return err
}
host, _, _ := net.SplitHostPort(hostAddressWithPort)
tlsConn := &tls.Config{
InsecureSkipVerify: true,
ServerName: host,
}
if err = client.StartTLS(tlsConn); err != nil {
return err
}
if auth != nil {
if err = client.Auth(auth); err != nil {
return err
}
}
if err = client.Mail(fromAddress); err != nil {
return err
}
for _, rec := range recipients {
if err = client.Rcpt(rec); err != nil {
return err
}
}
w, err := client.Data()
if err != nil {
return err
}
_, err = w.Write([]byte(msgContent))
if err != nil {
return err
}
err = w.Close()
if err != nil {
return err
}
err = client.Quit()
if err != nil {
return err
}
return nil
}
// write message in smtp writer.
// it will send an email with subject and only this message.
func (s *SmtpWriter) WriteMsg(msg string, level int) error {
@ -86,16 +143,10 @@ func (s *SmtpWriter) WriteMsg(msg string, level int) error {
// Connect to the server, authenticate, set the sender and recipient,
// and send the email all in one step.
content_type := "Content-Type: text/plain" + "; charset=UTF-8"
mailmsg := []byte("To: " + strings.Join(s.RecipientAddresses, ";") + "\r\nFrom: " + s.Username + "<" + s.Username +
mailmsg := []byte("To: " + strings.Join(s.RecipientAddresses, ";") + "\r\nFrom: " + s.FromAddress + "<" + s.FromAddress +
">\r\nSubject: " + s.Subject + "\r\n" + content_type + "\r\n\r\n" + fmt.Sprintf(".%s", time.Now().Format("2006-01-02 15:04:05")) + msg)
err := smtp.SendMail(
s.Host,
auth,
s.Username,
s.RecipientAddresses,
mailmsg,
)
err := s.sendMail(s.Host, auth, s.FromAddress, s.RecipientAddresses, mailmsg)
return err
}

104
orm/db.go
View File

@ -122,6 +122,12 @@ func (d *dbBase) collectFieldValue(mi *modelInfo, fi *fieldInfo, ind reflect.Val
if nb.Valid {
value = nb.Bool
}
} else if field.Kind() == reflect.Ptr {
if field.IsNil() {
value = nil
} else {
value = field.Elem().Bool()
}
} else {
value = field.Bool()
}
@ -131,6 +137,12 @@ func (d *dbBase) collectFieldValue(mi *modelInfo, fi *fieldInfo, ind reflect.Val
if ns.Valid {
value = ns.String
}
} else if field.Kind() == reflect.Ptr {
if field.IsNil() {
value = nil
} else {
value = field.Elem().String()
}
} else {
value = field.String()
}
@ -140,6 +152,12 @@ func (d *dbBase) collectFieldValue(mi *modelInfo, fi *fieldInfo, ind reflect.Val
if nf.Valid {
value = nf.Float64
}
} else if field.Kind() == reflect.Ptr {
if field.IsNil() {
value = nil
} else {
value = field.Elem().Float()
}
} else {
vu := field.Interface()
if _, ok := vu.(float32); ok {
@ -161,13 +179,27 @@ func (d *dbBase) collectFieldValue(mi *modelInfo, fi *fieldInfo, ind reflect.Val
default:
switch {
case fi.fieldType&IsPostiveIntegerField > 0:
value = field.Uint()
if field.Kind() == reflect.Ptr {
if field.IsNil() {
value = nil
} else {
value = field.Elem().Uint()
}
} else {
value = field.Uint()
}
case fi.fieldType&IsIntegerField > 0:
if ni, ok := field.Interface().(sql.NullInt64); ok {
value = nil
if ni.Valid {
value = ni.Int64
}
} else if field.Kind() == reflect.Ptr {
if field.IsNil() {
value = nil
} else {
value = field.Elem().Int()
}
} else {
value = field.Int()
}
@ -1177,6 +1209,11 @@ setValue:
nb.Valid = true
}
field.Set(reflect.ValueOf(nb))
} else if field.Kind() == reflect.Ptr {
if value != nil {
v := value.(bool)
field.Set(reflect.ValueOf(&v))
}
} else {
if value == nil {
value = false
@ -1194,6 +1231,11 @@ setValue:
ns.Valid = true
}
field.Set(reflect.ValueOf(ns))
} else if field.Kind() == reflect.Ptr {
if value != nil {
v := value.(string)
field.Set(reflect.ValueOf(&v))
}
} else {
if value == nil {
value = ""
@ -1208,6 +1250,56 @@ setValue:
}
field.Set(reflect.ValueOf(value))
}
case fieldType == TypePositiveBitField && field.Kind() == reflect.Ptr:
if value != nil {
v := uint8(value.(uint64))
field.Set(reflect.ValueOf(&v))
}
case fieldType == TypePositiveSmallIntegerField && field.Kind() == reflect.Ptr:
if value != nil {
v := uint16(value.(uint64))
field.Set(reflect.ValueOf(&v))
}
case fieldType == TypePositiveIntegerField && field.Kind() == reflect.Ptr:
if value != nil {
if field.Type() == reflect.TypeOf(new(uint)) {
v := uint(value.(uint64))
field.Set(reflect.ValueOf(&v))
} else {
v := uint32(value.(uint64))
field.Set(reflect.ValueOf(&v))
}
}
case fieldType == TypePositiveBigIntegerField && field.Kind() == reflect.Ptr:
if value != nil {
v := value.(uint64)
field.Set(reflect.ValueOf(&v))
}
case fieldType == TypeBitField && field.Kind() == reflect.Ptr:
if value != nil {
v := int8(value.(int64))
field.Set(reflect.ValueOf(&v))
}
case fieldType == TypeSmallIntegerField && field.Kind() == reflect.Ptr:
if value != nil {
v := int16(value.(int64))
field.Set(reflect.ValueOf(&v))
}
case fieldType == TypeIntegerField && field.Kind() == reflect.Ptr:
if value != nil {
if field.Type() == reflect.TypeOf(new(int)) {
v := int(value.(int64))
field.Set(reflect.ValueOf(&v))
} else {
v := int32(value.(int64))
field.Set(reflect.ValueOf(&v))
}
}
case fieldType == TypeBigIntegerField && field.Kind() == reflect.Ptr:
if value != nil {
v := value.(int64)
field.Set(reflect.ValueOf(&v))
}
case fieldType&IsIntegerField > 0:
if fieldType&IsPostiveIntegerField > 0 {
if isNative {
@ -1244,6 +1336,16 @@ setValue:
nf.Valid = true
}
field.Set(reflect.ValueOf(nf))
} else if field.Kind() == reflect.Ptr {
if value != nil {
if field.Type() == reflect.TypeOf(new(float32)) {
v := float32(value.(float64))
field.Set(reflect.ValueOf(&v))
} else {
v := value.(float64)
field.Set(reflect.ValueOf(&v))
}
}
} else {
if value == nil {

View File

@ -155,6 +155,24 @@ type DataNull struct {
NullBool sql.NullBool `orm:"null"`
NullFloat64 sql.NullFloat64 `orm:"null"`
NullInt64 sql.NullInt64 `orm:"null"`
BooleanPtr *bool `orm:"null"`
CharPtr *string `orm:"null;size(50)"`
TextPtr *string `orm:"null;type(text)"`
BytePtr *byte `orm:"null"`
RunePtr *rune `orm:"null"`
IntPtr *int `orm:"null"`
Int8Ptr *int8 `orm:"null"`
Int16Ptr *int16 `orm:"null"`
Int32Ptr *int32 `orm:"null"`
Int64Ptr *int64 `orm:"null"`
UintPtr *uint `orm:"null"`
Uint8Ptr *uint8 `orm:"null"`
Uint16Ptr *uint16 `orm:"null"`
Uint32Ptr *uint32 `orm:"null"`
Uint64Ptr *uint64 `orm:"null"`
Float32Ptr *float32 `orm:"null"`
Float64Ptr *float64 `orm:"null"`
DecimalPtr *float64 `orm:"digits(8);decimals(4);null"`
}
type String string

View File

@ -111,45 +111,73 @@ func getColumnName(ft int, addrField reflect.Value, sf reflect.StructField, col
// return field type as type constant from reflect.Value
func getFieldType(val reflect.Value) (ft int, err error) {
elm := reflect.Indirect(val)
switch elm.Kind() {
case reflect.Int8:
switch val.Type() {
case reflect.TypeOf(new(int8)):
ft = TypeBitField
case reflect.Int16:
case reflect.TypeOf(new(int16)):
ft = TypeSmallIntegerField
case reflect.Int32, reflect.Int:
case reflect.TypeOf(new(int32)),
reflect.TypeOf(new(int)):
ft = TypeIntegerField
case reflect.Int64:
case reflect.TypeOf(new(int64)):
ft = TypeBigIntegerField
case reflect.Uint8:
case reflect.TypeOf(new(uint8)):
ft = TypePositiveBitField
case reflect.Uint16:
case reflect.TypeOf(new(uint16)):
ft = TypePositiveSmallIntegerField
case reflect.Uint32, reflect.Uint:
case reflect.TypeOf(new(uint32)),
reflect.TypeOf(new(uint)):
ft = TypePositiveIntegerField
case reflect.Uint64:
case reflect.TypeOf(new(uint64)):
ft = TypePositiveBigIntegerField
case reflect.Float32, reflect.Float64:
case reflect.TypeOf(new(float32)),
reflect.TypeOf(new(float64)):
ft = TypeFloatField
case reflect.Bool:
case reflect.TypeOf(new(bool)):
ft = TypeBooleanField
case reflect.String:
case reflect.TypeOf(new(string)):
ft = TypeCharField
default:
if elm.Interface() == nil {
panic(fmt.Errorf("%s is nil pointer, may be miss setting tag", val))
}
switch elm.Interface().(type) {
case sql.NullInt64:
elm := reflect.Indirect(val)
switch elm.Kind() {
case reflect.Int8:
ft = TypeBitField
case reflect.Int16:
ft = TypeSmallIntegerField
case reflect.Int32, reflect.Int:
ft = TypeIntegerField
case reflect.Int64:
ft = TypeBigIntegerField
case sql.NullFloat64:
case reflect.Uint8:
ft = TypePositiveBitField
case reflect.Uint16:
ft = TypePositiveSmallIntegerField
case reflect.Uint32, reflect.Uint:
ft = TypePositiveIntegerField
case reflect.Uint64:
ft = TypePositiveBigIntegerField
case reflect.Float32, reflect.Float64:
ft = TypeFloatField
case sql.NullBool:
case reflect.Bool:
ft = TypeBooleanField
case sql.NullString:
case reflect.String:
ft = TypeCharField
case time.Time:
ft = TypeDateTimeField
default:
if elm.Interface() == nil {
panic(fmt.Errorf("%s is nil pointer, may be miss setting tag", val))
}
switch elm.Interface().(type) {
case sql.NullInt64:
ft = TypeBigIntegerField
case sql.NullFloat64:
ft = TypeFloatField
case sql.NullBool:
ft = TypeBooleanField
case sql.NullString:
ft = TypeCharField
case time.Time:
ft = TypeDateTimeField
}
}
}
if ft&IsFieldType == 0 {

View File

@ -287,6 +287,25 @@ func TestNullDataTypes(t *testing.T) {
throwFail(t, AssertIs(d.NullInt64.Valid, false))
throwFail(t, AssertIs(d.NullFloat64.Valid, false))
throwFail(t, AssertIs(d.BooleanPtr, nil))
throwFail(t, AssertIs(d.CharPtr, nil))
throwFail(t, AssertIs(d.TextPtr, nil))
throwFail(t, AssertIs(d.BytePtr, nil))
throwFail(t, AssertIs(d.RunePtr, nil))
throwFail(t, AssertIs(d.IntPtr, nil))
throwFail(t, AssertIs(d.Int8Ptr, nil))
throwFail(t, AssertIs(d.Int16Ptr, nil))
throwFail(t, AssertIs(d.Int32Ptr, nil))
throwFail(t, AssertIs(d.Int64Ptr, nil))
throwFail(t, AssertIs(d.UintPtr, nil))
throwFail(t, AssertIs(d.Uint8Ptr, nil))
throwFail(t, AssertIs(d.Uint16Ptr, nil))
throwFail(t, AssertIs(d.Uint32Ptr, nil))
throwFail(t, AssertIs(d.Uint64Ptr, nil))
throwFail(t, AssertIs(d.Float32Ptr, nil))
throwFail(t, AssertIs(d.Float64Ptr, nil))
throwFail(t, AssertIs(d.DecimalPtr, nil))
_, err = dORM.Raw(`INSERT INTO data_null (boolean) VALUES (?)`, nil).Exec()
throwFail(t, err)
@ -294,12 +313,49 @@ func TestNullDataTypes(t *testing.T) {
err = dORM.Read(&d)
throwFail(t, err)
booleanPtr := true
charPtr := string("test")
textPtr := string("test")
bytePtr := byte('t')
runePtr := rune('t')
intPtr := int(42)
int8Ptr := int8(42)
int16Ptr := int16(42)
int32Ptr := int32(42)
int64Ptr := int64(42)
uintPtr := uint(42)
uint8Ptr := uint8(42)
uint16Ptr := uint16(42)
uint32Ptr := uint32(42)
uint64Ptr := uint64(42)
float32Ptr := float32(42.0)
float64Ptr := float64(42.0)
decimalPtr := float64(42.0)
d = DataNull{
DateTime: time.Now(),
NullString: sql.NullString{String: "test", Valid: true},
NullBool: sql.NullBool{Bool: true, Valid: true},
NullInt64: sql.NullInt64{Int64: 42, Valid: true},
NullFloat64: sql.NullFloat64{Float64: 42.42, Valid: true},
BooleanPtr: &booleanPtr,
CharPtr: &charPtr,
TextPtr: &textPtr,
BytePtr: &bytePtr,
RunePtr: &runePtr,
IntPtr: &intPtr,
Int8Ptr: &int8Ptr,
Int16Ptr: &int16Ptr,
Int32Ptr: &int32Ptr,
Int64Ptr: &int64Ptr,
UintPtr: &uintPtr,
Uint8Ptr: &uint8Ptr,
Uint16Ptr: &uint16Ptr,
Uint32Ptr: &uint32Ptr,
Uint64Ptr: &uint64Ptr,
Float32Ptr: &float32Ptr,
Float64Ptr: &float64Ptr,
DecimalPtr: &decimalPtr,
}
id, err = dORM.Insert(&d)
@ -321,6 +377,25 @@ func TestNullDataTypes(t *testing.T) {
throwFail(t, AssertIs(d.NullFloat64.Valid, true))
throwFail(t, AssertIs(d.NullFloat64.Float64, 42.42))
throwFail(t, AssertIs(*d.BooleanPtr, booleanPtr))
throwFail(t, AssertIs(*d.CharPtr, charPtr))
throwFail(t, AssertIs(*d.TextPtr, textPtr))
throwFail(t, AssertIs(*d.BytePtr, bytePtr))
throwFail(t, AssertIs(*d.RunePtr, runePtr))
throwFail(t, AssertIs(*d.IntPtr, intPtr))
throwFail(t, AssertIs(*d.Int8Ptr, int8Ptr))
throwFail(t, AssertIs(*d.Int16Ptr, int16Ptr))
throwFail(t, AssertIs(*d.Int32Ptr, int32Ptr))
throwFail(t, AssertIs(*d.Int64Ptr, int64Ptr))
throwFail(t, AssertIs(*d.UintPtr, uintPtr))
throwFail(t, AssertIs(*d.Uint8Ptr, uint8Ptr))
throwFail(t, AssertIs(*d.Uint16Ptr, uint16Ptr))
throwFail(t, AssertIs(*d.Uint32Ptr, uint32Ptr))
throwFail(t, AssertIs(*d.Uint64Ptr, uint64Ptr))
throwFail(t, AssertIs(*d.Float32Ptr, float32Ptr))
throwFail(t, AssertIs(*d.Float64Ptr, float64Ptr))
throwFail(t, AssertIs(*d.DecimalPtr, decimalPtr))
}
func TestDataCustomTypes(t *testing.T) {

View File

@ -148,7 +148,7 @@ func genRouterCode() {
beego.GlobalControllerRouter["` + k + `"] = append(beego.GlobalControllerRouter["` + k + `"],
beego.ControllerComments{
"` + strings.TrimSpace(c.Method) + `",
"` + c.Router + `",
` + "`" + c.Router + "`" + `,
` + allmethod + `,
` + params + `})
`

176
plugins/apiauth/apiauth.go Normal file
View File

@ -0,0 +1,176 @@
// Copyright 2014 beego Author. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Package apiauth provides handlers to enable apiauth support.
//
// Simple Usage:
// import(
// "github.com/astaxie/beego"
// "github.com/astaxie/beego/plugins/apiauth"
// )
//
// func main(){
// // apiauth every request
// beego.InsertFilter("*", beego.BeforeRouter,apiauth.APIBaiscAuth("appid","appkey"))
// beego.Run()
// }
//
// Advanced Usage:
//
// func getAppSecret(appid string) string {
// // get appsecret by appid
// // maybe store in configure, maybe in database
// }
//
// beego.InsertFilter("*", beego.BeforeRouter,apiauth.APIAuthWithFunc(getAppSecret, 360))
//
// Infomation:
//
// In the request user should include these params in the query
//
// 1. appid
//
// appid is asigned to the application
//
// 2. signature
//
// get the signature use apiauth.Signature()
//
// when you send to server remember use url.QueryEscape()
//
// 3. timestamp:
//
// send the request time, the format is yyyy-mm-dd HH:ii:ss
//
package apiauth
import (
"crypto/hmac"
"crypto/sha256"
"encoding/base64"
"fmt"
"net/url"
"sort"
"time"
"github.com/astaxie/beego"
"github.com/astaxie/beego/context"
)
type AppIdToAppSecret func(string) string
func APIBaiscAuth(appid, appkey string) beego.FilterFunc {
ft := func(aid string) string {
if aid == appid {
return appkey
}
return ""
}
return APIAuthWithFunc(ft, 300)
}
func APIAuthWithFunc(f AppIdToAppSecret, timeout int) beego.FilterFunc {
return func(ctx *context.Context) {
if ctx.Input.Query("appid") == "" {
ctx.Output.SetStatus(403)
ctx.WriteString("miss query param: appid")
return
}
appsecret := f(ctx.Input.Query("appid"))
if appsecret == "" {
ctx.Output.SetStatus(403)
ctx.WriteString("not exist this appid")
return
}
if ctx.Input.Query("signature") == "" {
ctx.Output.SetStatus(403)
ctx.WriteString("miss query param: signature")
return
}
if ctx.Input.Query("timestamp") == "" {
ctx.Output.SetStatus(403)
ctx.WriteString("miss query param: timestamp")
return
}
u, err := time.Parse("2006-01-02 15:04:05", ctx.Input.Query("timestamp"))
if err != nil {
ctx.Output.SetStatus(403)
ctx.WriteString("timestamp format is error, should 2006-01-02 15:04:05")
return
}
t := time.Now()
if t.Sub(u).Seconds() > float64(timeout) {
ctx.Output.SetStatus(403)
ctx.WriteString("timeout! the request time is long ago, please try again")
return
}
if ctx.Input.Query("signature") !=
Signature(appsecret, ctx.Input.Method(), ctx.Request.Form, ctx.Input.Uri()) {
ctx.Output.SetStatus(403)
ctx.WriteString("auth failed")
}
}
}
func Signature(appsecret, method string, params url.Values, RequestURI string) (result string) {
var query string
pa := make(map[string]string)
for k, v := range params {
pa[k] = v[0]
}
vs := mapSorter(pa)
vs.Sort()
for i := 0; i < vs.Len(); i++ {
if vs.Keys[i] == "signature" {
continue
}
if vs.Keys[i] != "" && vs.Vals[i] != "" {
query = fmt.Sprintf("%v%v%v", query, vs.Keys[i], vs.Vals[i])
}
}
string_to_sign := fmt.Sprintf("%v\n%v\n%v\n", method, query, RequestURI)
sha256 := sha256.New
hash := hmac.New(sha256, []byte(appsecret))
hash.Write([]byte(string_to_sign))
return base64.StdEncoding.EncodeToString(hash.Sum(nil))
}
type valSorter struct {
Keys []string
Vals []string
}
func mapSorter(m map[string]string) *valSorter {
vs := &valSorter{
Keys: make([]string, 0, len(m)),
Vals: make([]string, 0, len(m)),
}
for k, v := range m {
vs.Keys = append(vs.Keys, k)
vs.Vals = append(vs.Vals, v)
}
return vs
}
func (vs *valSorter) Sort() {
sort.Sort(vs)
}
func (vs *valSorter) Len() int { return len(vs.Keys) }
func (vs *valSorter) Less(i, j int) bool { return vs.Keys[i] < vs.Keys[j] }
func (vs *valSorter) Swap(i, j int) {
vs.Vals[i], vs.Vals[j] = vs.Vals[j], vs.Vals[i]
vs.Keys[i], vs.Keys[j] = vs.Keys[j], vs.Keys[i]
}

View File

@ -27,6 +27,7 @@
//
//
// Advanced Usage:
//
// func SecretAuth(username, password string) bool {
// return username == "astaxie" && password == "helloBeego"
// }

View File

@ -818,8 +818,8 @@ func (p *ControllerRegistor) recoverPanic(rw http.ResponseWriter, r *http.Reques
if !ok {
break
}
Critical(file, line)
stack = stack + fmt.Sprintln(file, line)
Critical(fmt.Sprintf("%s:%d", file, line))
stack = stack + fmt.Sprintln(fmt.Sprintf("%s:%d", file, line))
}
middleware.ShowErr(err, rw, r, stack)
}
@ -840,7 +840,7 @@ func (p *ControllerRegistor) recoverPanic(rw http.ResponseWriter, r *http.Reques
if !ok {
break
}
Critical(file, line)
Critical(fmt.Sprintf("%s:%d", file, line))
}
}
}

View File

@ -83,13 +83,16 @@ func (m *UrlMap) AddStatistics(requestMethod, requestUrl, requestController stri
}
// put url statistics result in io.Writer
func (m *UrlMap) GetMap() [][]string {
func (m *UrlMap) GetMap() map[string]interface{} {
m.lock.RLock()
defer m.lock.RUnlock()
resultLists := make([][]string, 0)
var result = []string{"requestUrl", "method", "times", "used", "max used", "min used", "avg used"}
resultLists = append(resultLists, result)
var fields = []string{"requestUrl", "method", "times", "used", "max used", "min used", "avg used"}
resultLists := make([][]string, 0)
content := make(map[string]interface{})
content["Fields"] = fields
for k, v := range m.urlmap {
for kk, vv := range v {
result := []string{
@ -104,7 +107,8 @@ func (m *UrlMap) GetMap() [][]string {
resultLists = append(resultLists, result)
}
}
return resultLists
content["Data"] = resultLists
return content
}
// global statistics data map

View File

@ -33,6 +33,8 @@ type bounds struct {
var (
AdminTaskList map[string]Tasker
stop chan bool
changed chan bool
isstart bool
seconds = bounds{0, 59, nil}
minutes = bounds{0, 59, nil}
hours = bounds{0, 23, nil}
@ -379,6 +381,7 @@ func dayMatches(s *Schedule, t time.Time) bool {
// start all tasks
func StartTask() {
isstart = true
go run()
}
@ -411,6 +414,8 @@ func run() {
e.SetNext(effective)
}
continue
case <-changed:
continue
case <-stop:
return
}
@ -419,12 +424,24 @@ func run() {
// start all tasks
func StopTask() {
isstart = false
stop <- true
}
// add task with name
func AddTask(taskname string, t Tasker) {
AdminTaskList[taskname] = t
if isstart {
changed <- true
}
}
// add task with name
func DeleteTask(taskname string) {
delete(AdminTaskList, taskname)
if isstart {
changed <- true
}
}
// sort map for tasker
@ -578,4 +595,5 @@ func all(r bounds) uint64 {
func init() {
AdminTaskList = make(map[string]Tasker)
stop = make(chan bool)
changed = make(chan bool)
}

View File

@ -106,10 +106,10 @@ func SliceDiff(slice1, slice2 []interface{}) (diffslice []interface{}) {
return
}
// SliceIntersect returns diff slice of slice1 - slice2.
// SliceIntersect returns slice that are present in all the slice1 and slice2.
func SliceIntersect(slice1, slice2 []interface{}) (diffslice []interface{}) {
for _, v := range slice1 {
if !InSliceIface(v, slice2) {
if InSliceIface(v, slice2) {
diffslice = append(diffslice, v)
}
}