commit
81e14d12ea
54 changed files with 6829 additions and 0 deletions
@ -0,0 +1,4 @@ |
|||
#idea file |
|||
.idea |
|||
|
|||
dist |
|||
@ -0,0 +1,3 @@ |
|||
# 蓝眼云盘 |
|||
|
|||
|
|||
@ -0,0 +1,12 @@ |
|||
server{ |
|||
listen 80; |
|||
server_name tank.zicpo.cn; |
|||
|
|||
location / { |
|||
proxy_pass http://127.0.0.1:9090; |
|||
proxy_set_header host $host; |
|||
proxy_set_header X-Forwarded-For $remote_addr; |
|||
proxy_pass_request_headers on; |
|||
client_max_body_size 2048m; |
|||
} |
|||
} |
|||
@ -0,0 +1,12 @@ |
|||
{ |
|||
"ServerPort": 9090, |
|||
"LogToConsole": true, |
|||
"MysqlPort": 3306, |
|||
"MysqlHost": "127.0.0.1", |
|||
"MysqlSchema": "tank", |
|||
"MysqlUserName": "tank", |
|||
"MysqlPassword": "tank123", |
|||
"AdminUsername": "admin", |
|||
"AdminEmail": "admin@tank.eyeblue.cn", |
|||
"AdminPassword": "123456" |
|||
} |
|||
@ -0,0 +1 @@ |
|||
<!DOCTYPE html><html><head><title>tank-front</title><meta charset=utf-8><meta http-equiv=X-UA-Compatible content="IE=edge,chrome=1"><meta name=renderer content=webkit><meta name=viewport content="user-scalable=no,width=device-width,initial-scale=1,maximum-scale=1"><meta name=msapplication-tap-highlight content=no><meta name=apple-mobile-web-app-capable content=yes><link href=/static/css/app.cfd8f574917fd86fad8effb02f5563b2.css rel=stylesheet></head><body><div id=app></div><script type=text/javascript src=/static/js/manifest.67df5041971dde987922.js></script><script type=text/javascript src=/static/js/vendor.392879af4bb24a810ba3.js></script><script type=text/javascript src=/static/js/app.64233513a41451689d54.js></script></body></html> |
|||
File diff suppressed because one or more lines are too long
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
After Width: | Height: | Size: 434 KiB |
|
After Width: | Height: | Size: 1.4 MiB |
|
After Width: | Height: | Size: 134 KiB |
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -0,0 +1,2 @@ |
|||
!function(e){function __webpack_require__(r){if(_[r])return _[r].exports;var t=_[r]={i:r,l:!1,exports:{}};return e[r].call(t.exports,t,t.exports,__webpack_require__),t.l=!0,t.exports}var r=window.webpackJsonp;window.webpackJsonp=function(_,n,o){for(var c,a,i,u=0,p=[];u<_.length;u++)a=_[u],t[a]&&p.push(t[a][0]),t[a]=0;for(c in n)Object.prototype.hasOwnProperty.call(n,c)&&(e[c]=n[c]);for(r&&r(_,n,o);p.length;)p.shift()();if(o)for(u=0;u<o.length;u++)i=__webpack_require__(__webpack_require__.s=o[u]);return i};var _={},t={2:0};__webpack_require__.e=function(e){function onScriptComplete(){o.onerror=o.onload=null,clearTimeout(c);var r=t[e];0!==r&&(r&&r[1](new Error("Loading chunk "+e+" failed.")),t[e]=void 0)}var r=t[e];if(0===r)return new Promise(function(e){e()});if(r)return r[2];var _=new Promise(function(_,n){r=t[e]=[_,n]});r[2]=_;var n=document.getElementsByTagName("head")[0],o=document.createElement("script");o.type="text/javascript",o.charset="utf-8",o.async=!0,o.timeout=12e4,__webpack_require__.nc&&o.setAttribute("nonce",__webpack_require__.nc),o.src=__webpack_require__.p+"static/js/"+e+"."+{0:"392879af4bb24a810ba3",1:"64233513a41451689d54"}[e]+".js";var c=setTimeout(onScriptComplete,12e4);return o.onerror=o.onload=onScriptComplete,n.appendChild(o),_},__webpack_require__.m=e,__webpack_require__.c=_,__webpack_require__.d=function(exports,e,r){__webpack_require__.o(exports,e)||Object.defineProperty(exports,e,{configurable:!1,enumerable:!0,get:r})},__webpack_require__.n=function(e){var r=e&&e.__esModule?function(){return e.default}:function(){return e};return __webpack_require__.d(r,"a",r),r},__webpack_require__.o=function(e,r){return Object.prototype.hasOwnProperty.call(e,r)},__webpack_require__.p="/",__webpack_require__.oe=function(e){throw console.error(e),e}}([]); |
|||
//# sourceMappingURL=manifest.67df5041971dde987922.js.map
|
|||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -0,0 +1,68 @@ |
|||
@if "%DEBUG%" == "" echo off |
|||
@rem ########################################################################## |
|||
@rem |
|||
@rem Tank build script for Windows |
|||
@rem |
|||
@rem ########################################################################## |
|||
|
|||
|
|||
|
|||
@REM ==== START VALIDATION ==== |
|||
if "%GOPATH%"=="" ( |
|||
echo The GOPATH environment variable is not defined correctly |
|||
goto end |
|||
) |
|||
|
|||
set PRE_DIR=%cd% |
|||
|
|||
cd %GOPATH% |
|||
|
|||
echo golang.org . Please download from: https://github.com/MXi4oyu/golang.org and put in the directory with same level of github.com |
|||
@rem echo go get golang.org/x |
|||
@rem go get golang.org/x |
|||
|
|||
@rem resize image |
|||
echo go get github.com/disintegration/imaging |
|||
go get github.com/disintegration/imaging |
|||
|
|||
@rem json parser |
|||
echo go get github.com/json-iterator/go |
|||
go get github.com/json-iterator/go |
|||
|
|||
|
|||
@rem mysql |
|||
echo go get github.com/go-sql-driver/mysql |
|||
go get github.com/go-sql-driver/mysql |
|||
|
|||
@rem dao database |
|||
echo go get github.com/jinzhu/gorm |
|||
go get github.com/jinzhu/gorm |
|||
|
|||
|
|||
@rem uuid |
|||
echo go get github.com/nu7hatch/gouuid |
|||
go get github.com/nu7hatch/gouuid |
|||
|
|||
echo build tank ... |
|||
go install tank |
|||
|
|||
echo packaging |
|||
set distPath=%GOPATH%\src\tank\dist |
|||
if exist %distPath% ( |
|||
echo clear %distPath% |
|||
rmdir /s/q %distPath% |
|||
) |
|||
|
|||
echo create directory %distPath% |
|||
md %distPath% |
|||
|
|||
echo copying tank.exe |
|||
copy %GOPATH%\bin\tank.exe %distPath% |
|||
|
|||
echo copying build |
|||
xcopy %GOPATH%\src\tank\build %distPath% /e/h |
|||
|
|||
cd %PRE_DIR% |
|||
|
|||
echo check the dist file in %distPath% |
|||
echo finish! |
|||
@ -0,0 +1,14 @@ |
|||
#!/bin/bash |
|||
|
|||
|
|||
EXE_PATH=$GOPATH/bin/tank |
|||
|
|||
EDASPID=`ps -ef | grep "$EXE_PATH"|grep -v grep |head -n 1 | awk '{print $2}'` |
|||
if [ -z $EDASPID ]; |
|||
then |
|||
echo "Cannot find $EXE_PATH." |
|||
else |
|||
kill -9 $EDASPID |
|||
echo $EXE_PATH |
|||
echo 'Shutdown successfully.' |
|||
fi |
|||
@ -0,0 +1,22 @@ |
|||
#!/bin/bash |
|||
|
|||
# executable path |
|||
EXE_PATH=$GOPATH/bin/tank |
|||
|
|||
# execute arguments |
|||
MysqlHost=127.0.0.1 |
|||
MysqlPort=3306 |
|||
MysqlSchema=tank |
|||
MysqlUserName=tank |
|||
MysqlPassword=Tank_123 |
|||
|
|||
AdminUsername=admin |
|||
AdminEmail=lish516@126.com |
|||
AdminPassword=123456 |
|||
|
|||
if [ -f "$EXE_PATH" ]; then |
|||
nohup $EXE_PATH -MysqlHost=$MysqlHost -MysqlPort=$MysqlPort -MysqlSchema=$MysqlSchema -MysqlUserName=$MysqlUserName -MysqlPassword=$MysqlPassword -AdminUsername=$AdminUsername -AdminEmail=$AdminEmail -AdminPassword=$AdminPassword >/dev/null 2>&1 & |
|||
else |
|||
echo 'Cannot find $EXE_PATH.' |
|||
exit 1 |
|||
fi |
|||
@ -0,0 +1,27 @@ |
|||
#!/bin/sh |
|||
|
|||
homePath=$GOPATH/src/tank |
|||
|
|||
oldPath=$(pwd) |
|||
|
|||
echo "cd homePath" |
|||
cd $homePath |
|||
|
|||
echo "shutdown tank" |
|||
source $homePath/doc/script/shutdown.sh |
|||
|
|||
echo "git reset" |
|||
git reset --hard HEAD |
|||
|
|||
echo "git pull" |
|||
git pull |
|||
|
|||
echo "go install tank" |
|||
go install tank |
|||
|
|||
cd $oldPath |
|||
|
|||
echo "startup tank" |
|||
source $homePath/doc/script/startup.sh |
|||
|
|||
|
|||
@ -0,0 +1,29 @@ |
|||
package main |
|||
|
|||
import ( |
|||
"fmt" |
|||
_ "github.com/go-sql-driver/mysql" |
|||
"log" |
|||
"net/http" |
|||
"tank/rest" |
|||
) |
|||
|
|||
func main() { |
|||
|
|||
//将运行时参数装填到config中去。
|
|||
rest.PrepareConfigs() |
|||
context := rest.NewContext() |
|||
defer context.Destroy() |
|||
|
|||
http.Handle("/", context.Router) |
|||
|
|||
dotPort := fmt.Sprintf(":%v", rest.CONFIG.ServerPort) |
|||
|
|||
info := fmt.Sprintf("App started at http://localhost%v", dotPort) |
|||
rest.LogInfo(info) |
|||
|
|||
err := http.ListenAndServe(dotPort, nil) |
|||
if err != nil { |
|||
log.Fatal("ListenAndServe: ", err) |
|||
} |
|||
} |
|||
@ -0,0 +1,459 @@ |
|||
package rest |
|||
|
|||
import ( |
|||
"fmt" |
|||
"github.com/disintegration/imaging" |
|||
"io" |
|||
"net/http" |
|||
"net/url" |
|||
"os" |
|||
"regexp" |
|||
"strconv" |
|||
"strings" |
|||
"time" |
|||
) |
|||
|
|||
type AlienController struct { |
|||
BaseController |
|||
uploadTokenDao *UploadTokenDao |
|||
downloadTokenDao *DownloadTokenDao |
|||
matterDao *MatterDao |
|||
matterService *MatterService |
|||
} |
|||
|
|||
//初始化方法
|
|||
func (this *AlienController) Init(context *Context) { |
|||
this.BaseController.Init(context) |
|||
|
|||
//手动装填本实例的Bean.
|
|||
b := context.GetBean(this.uploadTokenDao) |
|||
if c, ok := b.(*UploadTokenDao); ok { |
|||
this.uploadTokenDao = c |
|||
} |
|||
|
|||
b = context.GetBean(this.downloadTokenDao) |
|||
if c, ok := b.(*DownloadTokenDao); ok { |
|||
this.downloadTokenDao = c |
|||
} |
|||
|
|||
b = context.GetBean(this.matterDao) |
|||
if c, ok := b.(*MatterDao); ok { |
|||
this.matterDao = c |
|||
} |
|||
|
|||
b = context.GetBean(this.matterService) |
|||
if c, ok := b.(*MatterService); ok { |
|||
this.matterService = c |
|||
} |
|||
} |
|||
|
|||
//注册自己的路由。
|
|||
func (this *AlienController) RegisterRoutes() map[string]func(writer http.ResponseWriter, request *http.Request) { |
|||
|
|||
routeMap := make(map[string]func(writer http.ResponseWriter, request *http.Request)) |
|||
|
|||
//每个Controller需要主动注册自己的路由。
|
|||
routeMap["/api/alien/fetch/upload/token"] = this.Wrap(this.FetchUploadToken, USER_ROLE_GUEST) |
|||
routeMap["/api/alien/fetch/download/token"] = this.Wrap(this.FetchDownloadToken, USER_ROLE_GUEST) |
|||
routeMap["/api/alien/confirm"] = this.Wrap(this.Confirm, USER_ROLE_GUEST) |
|||
routeMap["/api/alien/upload"] = this.Wrap(this.Upload, USER_ROLE_GUEST) |
|||
|
|||
return routeMap |
|||
} |
|||
|
|||
//处理一些特殊的接口,比如参数包含在路径中,一般情况下,controller不将参数放在url路径中
|
|||
func (this *AlienController) HandleRoutes(writer http.ResponseWriter, request *http.Request) (func(writer http.ResponseWriter, request *http.Request), bool) { |
|||
|
|||
path := request.URL.Path |
|||
|
|||
//匹配 /api/alien/download/{uuid}/{filename}
|
|||
reg := regexp.MustCompile(`^/api/alien/download/([^/]+)/([^/]+)$`) |
|||
strs := reg.FindStringSubmatch(path) |
|||
if len(strs) != 3 { |
|||
return nil, false |
|||
} else { |
|||
var f = func(writer http.ResponseWriter, request *http.Request) { |
|||
this.Download(writer, request, strs[1], strs[2]) |
|||
} |
|||
return f, true |
|||
} |
|||
} |
|||
|
|||
//直接使用邮箱和密码获取用户
|
|||
func (this *AlienController) CheckRequestUser(email, password string) *User { |
|||
|
|||
if email == "" { |
|||
panic("邮箱必填啦") |
|||
} |
|||
|
|||
if password == "" { |
|||
panic("密码必填") |
|||
} |
|||
|
|||
//验证用户身份合法性。
|
|||
user := this.userDao.FindByEmail(email) |
|||
if user == nil { |
|||
panic(`邮箱或密码错误`) |
|||
} else { |
|||
if !MatchBcrypt(password, user.Password) { |
|||
panic(`邮箱或密码错误`) |
|||
} |
|||
} |
|||
return user |
|||
} |
|||
|
|||
//系统中的用户x要获取一个UploadToken,用于提供给x信任的用户上传文件。
|
|||
func (this *AlienController) FetchUploadToken(writer http.ResponseWriter, request *http.Request) *WebResult { |
|||
|
|||
//文件名。
|
|||
filename := request.FormValue("filename") |
|||
if filename == "" { |
|||
panic("文件名必填") |
|||
} else if m, _ := regexp.MatchString(`[<>|*?/\\]`, filename); m { |
|||
panic(fmt.Sprintf(`【%s】不符合要求,文件名中不能包含以下特殊符号:< > | * ? / \`, filename)) |
|||
} |
|||
|
|||
//什么时间后过期,默认24h
|
|||
expireStr := request.FormValue("expire") |
|||
expire := 24 * 60 * 60 |
|||
if expireStr != "" { |
|||
var err error |
|||
expire, err = strconv.Atoi(expireStr) |
|||
if err != nil { |
|||
panic(`过期时间不符合规范`) |
|||
} |
|||
if expire < 1 { |
|||
panic(`过期时间不符合规范`) |
|||
} |
|||
|
|||
} |
|||
|
|||
//文件公有或私有
|
|||
privacyStr := request.FormValue("privacy") |
|||
var privacy bool |
|||
if privacyStr == "" { |
|||
panic(`文件公有性必填`) |
|||
} else { |
|||
if privacyStr == "true" { |
|||
privacy = true |
|||
} else if privacyStr == "false" { |
|||
privacy = false |
|||
} else { |
|||
panic(`文件公有性不符合规范`) |
|||
} |
|||
} |
|||
|
|||
//文件大小
|
|||
sizeStr := request.FormValue("size") |
|||
var size int64 |
|||
if sizeStr == "" { |
|||
panic(`文件大小必填`) |
|||
} else { |
|||
|
|||
var err error |
|||
size, err = strconv.ParseInt(sizeStr, 10, 64) |
|||
if err != nil { |
|||
panic(`文件大小不符合规范`) |
|||
} |
|||
if size < 1 { |
|||
panic(`文件大小不符合规范`) |
|||
} |
|||
} |
|||
|
|||
//文件夹路径,以 / 开头。
|
|||
dir := request.FormValue("dir") |
|||
|
|||
user := this.CheckRequestUser(request.FormValue("email"), request.FormValue("password")) |
|||
dirUuid := this.matterService.GetDirUuid(user.Uuid, dir) |
|||
|
|||
mm, _ := time.ParseDuration(fmt.Sprintf("%ds", expire)) |
|||
uploadToken := &UploadToken{ |
|||
UserUuid: user.Uuid, |
|||
FolderUuid: dirUuid, |
|||
MatterUuid: "", |
|||
ExpireTime: time.Now().Add(mm), |
|||
Filename: filename, |
|||
Privacy: privacy, |
|||
Size: size, |
|||
Ip: GetIpAddress(request), |
|||
} |
|||
|
|||
uploadToken = this.uploadTokenDao.Create(uploadToken) |
|||
|
|||
return this.Success(uploadToken) |
|||
|
|||
} |
|||
|
|||
//系统中的用户x 拿着某个文件的uuid来确认是否其信任的用户已经上传好了。
|
|||
func (this *AlienController) Confirm(writer http.ResponseWriter, request *http.Request) *WebResult { |
|||
|
|||
matterUuid := request.FormValue("matterUuid") |
|||
if matterUuid == "" { |
|||
panic("matterUuid必填") |
|||
} |
|||
|
|||
user := this.CheckRequestUser(request.FormValue("email"), request.FormValue("password")) |
|||
|
|||
matter := this.matterDao.CheckByUuid(matterUuid) |
|||
if matter.UserUuid != user.Uuid { |
|||
panic("文件不属于你") |
|||
} |
|||
|
|||
return this.Success(matter) |
|||
} |
|||
|
|||
//系统中的用户x 信任的用户上传文件。这个接口需要支持跨域。
|
|||
func (this *AlienController) Upload(writer http.ResponseWriter, request *http.Request) *WebResult { |
|||
//允许跨域请求。
|
|||
this.allowCORS(writer) |
|||
if request.Method == "OPTIONS" { |
|||
return this.Success("OK") |
|||
} |
|||
|
|||
uploadTokenUuid := request.FormValue("uploadTokenUuid") |
|||
if uploadTokenUuid == "" { |
|||
panic("uploadTokenUuid必填") |
|||
} |
|||
|
|||
uploadToken := this.uploadTokenDao.FindByUuid(uploadTokenUuid) |
|||
if uploadToken == nil { |
|||
panic("uploadTokenUuid无效") |
|||
} |
|||
|
|||
if uploadToken.ExpireTime.Before(time.Now()) { |
|||
panic("uploadToken已失效") |
|||
} |
|||
|
|||
user := this.userDao.CheckByUuid(uploadToken.UserUuid) |
|||
|
|||
request.ParseMultipartForm(32 << 20) |
|||
file, handler, err := request.FormFile("file") |
|||
this.PanicError(err) |
|||
defer file.Close() |
|||
|
|||
if handler.Filename != uploadToken.Filename { |
|||
panic("文件名称不正确") |
|||
} |
|||
|
|||
if handler.Size != uploadToken.Size { |
|||
panic("文件大小不正确") |
|||
} |
|||
|
|||
matter := this.matterService.Upload(file, user, uploadToken.FolderUuid, uploadToken.Filename, uploadToken.Privacy) |
|||
|
|||
//更新这个uploadToken的信息.
|
|||
uploadToken.ExpireTime = time.Now() |
|||
this.uploadTokenDao.Save(uploadToken) |
|||
|
|||
return this.Success(matter) |
|||
} |
|||
|
|||
//系统中的用户x要获取一个DownloadToken,用于提供给x信任的用户下载文件。
|
|||
func (this *AlienController) FetchDownloadToken(writer http.ResponseWriter, request *http.Request) *WebResult { |
|||
|
|||
matterUuid := request.FormValue("matterUuid") |
|||
if matterUuid == "" { |
|||
panic("matterUuid必填") |
|||
} |
|||
|
|||
user := this.CheckRequestUser(request.FormValue("email"), request.FormValue("password")) |
|||
|
|||
matter := this.matterDao.CheckByUuid(matterUuid) |
|||
if matter.UserUuid != user.Uuid { |
|||
panic("文件不属于你") |
|||
} |
|||
if matter.Dir { |
|||
panic("不支持下载文件夹") |
|||
} |
|||
|
|||
//什么时间后过期,默认24h
|
|||
expireStr := request.FormValue("expire") |
|||
expire := 24 * 60 * 60 |
|||
if expireStr != "" { |
|||
var err error |
|||
expire, err = strconv.Atoi(expireStr) |
|||
if err != nil { |
|||
panic(`过期时间不符合规范`) |
|||
} |
|||
if expire < 1 { |
|||
panic(`过期时间不符合规范`) |
|||
} |
|||
|
|||
} |
|||
|
|||
mm, _ := time.ParseDuration(fmt.Sprintf("%ds", expire)) |
|||
downloadToken := &DownloadToken{ |
|||
UserUuid: user.Uuid, |
|||
MatterUuid: matterUuid, |
|||
ExpireTime: time.Now().Add(mm), |
|||
Ip: GetIpAddress(request), |
|||
} |
|||
|
|||
downloadToken = this.downloadTokenDao.Create(downloadToken) |
|||
|
|||
return this.Success(downloadToken) |
|||
|
|||
} |
|||
|
|||
//下载一个文件。既可以使用登录的方式下载,也可以使用授权的方式下载。
|
|||
func (this *AlienController) Download(writer http.ResponseWriter, request *http.Request, uuid string, filename string) { |
|||
|
|||
matter := this.matterDao.CheckByUuid(uuid) |
|||
|
|||
//判断是否是文件夹
|
|||
if matter.Dir { |
|||
panic("暂不支持下载文件夹") |
|||
} |
|||
|
|||
if matter.Name != filename { |
|||
panic("文件信息错误") |
|||
} |
|||
|
|||
//验证用户的权限问题。
|
|||
//文件如果是私有的才需要权限
|
|||
if matter.Privacy { |
|||
|
|||
//1.如果带有downloadTokenUuid那么就按照token的信息去获取。
|
|||
downloadTokenUuid := request.FormValue("downloadTokenUuid") |
|||
if downloadTokenUuid != "" { |
|||
downloadToken := this.downloadTokenDao.CheckByUuid(downloadTokenUuid) |
|||
if downloadToken.ExpireTime.Before(time.Now()) { |
|||
panic("downloadToken已失效") |
|||
} |
|||
|
|||
if downloadToken.MatterUuid != uuid { |
|||
panic("token和文件信息不一致") |
|||
} |
|||
|
|||
tokenUser := this.userDao.CheckByUuid(downloadToken.UserUuid) |
|||
if matter.UserUuid != tokenUser.Uuid { |
|||
panic(RESULT_CODE_UNAUTHORIZED) |
|||
} |
|||
|
|||
//下载之后立即过期掉。
|
|||
downloadToken.ExpireTime = time.Now() |
|||
this.downloadTokenDao.Save(downloadToken) |
|||
|
|||
} else { |
|||
|
|||
//判断文件的所属人是否正确
|
|||
user := this.checkUser(writer, request) |
|||
if user.Role != USER_ROLE_ADMINISTRATOR && matter.UserUuid != user.Uuid { |
|||
panic(RESULT_CODE_UNAUTHORIZED) |
|||
} |
|||
|
|||
} |
|||
} |
|||
|
|||
diskFile, err := os.Open(GetFilePath() + matter.Path) |
|||
this.PanicError(err) |
|||
defer diskFile.Close() |
|||
// 防止中文乱码
|
|||
fileName := url.QueryEscape(matter.Name) |
|||
writer.Header().Set("Content-Type", GetMimeType(fileName)) |
|||
|
|||
//如果是图片或者文本就直接打开。
|
|||
mimeType := GetMimeType(matter.Name) |
|||
if strings.Index(mimeType, "image") != 0 && strings.Index(mimeType, "text") != 0 { |
|||
writer.Header().Set("content-disposition", "attachment; filename=\""+fileName+"\"") |
|||
} |
|||
|
|||
//对图片做缩放处理。
|
|||
imageProcess := request.FormValue("imageProcess") |
|||
if imageProcess == "resize" { |
|||
|
|||
//当前的文件是否是图片,只有图片才能处理。
|
|||
extension := GetExtension(matter.Name) |
|||
formats := map[string]imaging.Format{ |
|||
".jpg": imaging.JPEG, |
|||
".jpeg": imaging.JPEG, |
|||
".png": imaging.PNG, |
|||
".tif": imaging.TIFF, |
|||
".tiff": imaging.TIFF, |
|||
".bmp": imaging.BMP, |
|||
".gif": imaging.GIF, |
|||
} |
|||
|
|||
format, ok := formats[extension] |
|||
if !ok { |
|||
panic("该图片格式不支持处理") |
|||
} |
|||
|
|||
imageResizeM := request.FormValue("imageResizeM") |
|||
if imageResizeM == "" { |
|||
imageResizeM = "fit" |
|||
} else if imageResizeM != "fit" && imageResizeM != "fill" && imageResizeM != "fixed" { |
|||
panic("imageResizeM参数错误") |
|||
} |
|||
imageResizeWStr := request.FormValue("imageResizeW") |
|||
var imageResizeW int |
|||
if imageResizeWStr != "" { |
|||
imageResizeW, err = strconv.Atoi(imageResizeWStr) |
|||
this.PanicError(err) |
|||
if imageResizeW < 1 || imageResizeW > 4096 { |
|||
panic("缩放尺寸不能超过4096") |
|||
} |
|||
} |
|||
imageResizeHStr := request.FormValue("imageResizeH") |
|||
var imageResizeH int |
|||
if imageResizeHStr != "" { |
|||
imageResizeH, err = strconv.Atoi(imageResizeHStr) |
|||
this.PanicError(err) |
|||
if imageResizeH < 1 || imageResizeH > 4096 { |
|||
panic("缩放尺寸不能超过4096") |
|||
} |
|||
} |
|||
|
|||
//单边缩略
|
|||
if imageResizeM == "fit" { |
|||
//将图缩略成宽度为100,高度按比例处理。
|
|||
if imageResizeW > 0 { |
|||
src, err := imaging.Decode(diskFile) |
|||
this.PanicError(err) |
|||
dst := imaging.Resize(src, imageResizeW, 0, imaging.Lanczos) |
|||
|
|||
err = imaging.Encode(writer, dst, format) |
|||
this.PanicError(err) |
|||
} else if imageResizeH > 0 { |
|||
//将图缩略成高度为100,宽度按比例处理。
|
|||
src, err := imaging.Decode(diskFile) |
|||
this.PanicError(err) |
|||
dst := imaging.Resize(src, 0, imageResizeH, imaging.Lanczos) |
|||
|
|||
err = imaging.Encode(writer, dst, format) |
|||
this.PanicError(err) |
|||
} else { |
|||
panic("单边缩略必须指定imageResizeW或imageResizeH") |
|||
} |
|||
} else if imageResizeM == "fill" { |
|||
//固定宽高,自动裁剪
|
|||
if imageResizeW > 0 && imageResizeH > 0 { |
|||
src, err := imaging.Decode(diskFile) |
|||
this.PanicError(err) |
|||
dst := imaging.Fill(src, imageResizeW, imageResizeH, imaging.Center, imaging.Lanczos) |
|||
err = imaging.Encode(writer, dst, format) |
|||
this.PanicError(err) |
|||
} else { |
|||
panic("固定宽高,自动裁剪 必须同时指定imageResizeW和imageResizeH") |
|||
} |
|||
} else if imageResizeM == "fixed" { |
|||
//强制宽高缩略
|
|||
if imageResizeW > 0 && imageResizeH > 0 { |
|||
src, err := imaging.Decode(diskFile) |
|||
this.PanicError(err) |
|||
dst := imaging.Resize(src, imageResizeW, imageResizeH, imaging.Lanczos) |
|||
|
|||
err = imaging.Encode(writer, dst, format) |
|||
this.PanicError(err) |
|||
} else { |
|||
panic("强制宽高缩略必须同时指定imageResizeW和imageResizeH") |
|||
} |
|||
} |
|||
} else { |
|||
|
|||
_, err = io.Copy(writer, diskFile) |
|||
this.PanicError(err) |
|||
|
|||
} |
|||
|
|||
} |
|||
@ -0,0 +1,169 @@ |
|||
package rest |
|||
|
|||
import ( |
|||
"fmt" |
|||
"github.com/json-iterator/go" |
|||
"go/types" |
|||
"net/http" |
|||
"time" |
|||
) |
|||
|
|||
type IController interface { |
|||
IBean |
|||
//注册自己固定的路由。
|
|||
RegisterRoutes() map[string]func(writer http.ResponseWriter, request *http.Request) |
|||
//处理一些特殊的路由。
|
|||
HandleRoutes(writer http.ResponseWriter, request *http.Request) (func(writer http.ResponseWriter, request *http.Request), bool) |
|||
} |
|||
type BaseController struct { |
|||
Bean |
|||
userDao *UserDao |
|||
sessionDao *SessionDao |
|||
} |
|||
|
|||
func (this *BaseController) Init(context *Context) { |
|||
|
|||
this.Bean.Init(context) |
|||
|
|||
//手动装填本实例的Bean.
|
|||
b := context.GetBean(this.userDao) |
|||
if b, ok := b.(*UserDao); ok { |
|||
this.userDao = b |
|||
} |
|||
|
|||
b = context.GetBean(this.sessionDao) |
|||
if b, ok := b.(*SessionDao); ok { |
|||
this.sessionDao = b |
|||
} |
|||
|
|||
} |
|||
|
|||
//注册自己的路由。
|
|||
func (this *BaseController) RegisterRoutes() map[string]func(writer http.ResponseWriter, request *http.Request) { |
|||
//每个Controller需要主动注册自己的路由。
|
|||
return make(map[string]func(writer http.ResponseWriter, request *http.Request)) |
|||
} |
|||
|
|||
//处理一些特殊的接口,比如参数包含在路径中,一般情况下,controller不将参数放在url路径中
|
|||
func (this *BaseController) HandleRoutes(writer http.ResponseWriter, request *http.Request) (func(writer http.ResponseWriter, request *http.Request), bool) { |
|||
return nil, false |
|||
} |
|||
|
|||
//需要进行登录验证的wrap包装
|
|||
func (this *BaseController) Wrap(f func(writer http.ResponseWriter, request *http.Request) *WebResult, role string) func(w http.ResponseWriter, r *http.Request) { |
|||
|
|||
return func(writer http.ResponseWriter, request *http.Request) { |
|||
|
|||
//writer和request赋值给自己。
|
|||
|
|||
var webResult *WebResult = nil |
|||
|
|||
//只有游客接口不需要登录
|
|||
if role != USER_ROLE_GUEST { |
|||
user := this.checkUser(writer, request) |
|||
if role == USER_ROLE_ADMINISTRATOR && user.Role != USER_ROLE_ADMINISTRATOR { |
|||
webResult = ConstWebResult(RESULT_CODE_UNAUTHORIZED) |
|||
} else { |
|||
webResult = f(writer, request) |
|||
} |
|||
} else { |
|||
webResult = f(writer, request) |
|||
} |
|||
|
|||
//输出的是json格式
|
|||
if webResult != nil { |
|||
//返回的内容申明是json,utf-8
|
|||
writer.Header().Set("Content-Type", "application/json;charset=UTF-8") |
|||
|
|||
//用json的方式输出返回值。
|
|||
var json = jsoniter.ConfigCompatibleWithStandardLibrary |
|||
b, _ := json.Marshal(webResult) |
|||
|
|||
if webResult.Code == RESULT_CODE_OK { |
|||
writer.WriteHeader(http.StatusOK) |
|||
} else { |
|||
writer.WriteHeader(http.StatusBadRequest) |
|||
} |
|||
|
|||
fmt.Fprintf(writer, string(b)) |
|||
} else { |
|||
//输出的内容是二进制的。
|
|||
|
|||
} |
|||
|
|||
} |
|||
} |
|||
|
|||
//返回成功的结果。
|
|||
func (this *BaseController) Success(data interface{}) *WebResult { |
|||
var webResult *WebResult = nil |
|||
if value, ok := data.(string); ok { |
|||
webResult = &WebResult{Code: RESULT_CODE_OK, Msg: value} |
|||
} else if value, ok := data.(*WebResult); ok { |
|||
webResult = value |
|||
} else if _, ok := data.(types.Nil); ok { |
|||
webResult = ConstWebResult(RESULT_CODE_OK) |
|||
} else { |
|||
webResult = &WebResult{Code: RESULT_CODE_OK, Data: data} |
|||
} |
|||
return webResult |
|||
} |
|||
|
|||
//返回错误的结果。
|
|||
func (this *BaseController) Error(err interface{}) *WebResult { |
|||
var webResult *WebResult = nil |
|||
if value, ok := err.(string); ok { |
|||
webResult = &WebResult{Code: RESULT_CODE_UTIL_EXCEPTION, Msg: value} |
|||
} else if value, ok := err.(int); ok { |
|||
webResult = ConstWebResult(value) |
|||
} else if value, ok := err.(*WebResult); ok { |
|||
webResult = value |
|||
} else if value, ok := err.(error); ok { |
|||
webResult = &WebResult{Code: RESULT_CODE_UTIL_EXCEPTION, Msg: value.Error()} |
|||
} else { |
|||
webResult = &WebResult{Code: RESULT_CODE_UTIL_EXCEPTION, Msg: "服务器未知错误"} |
|||
} |
|||
return webResult |
|||
} |
|||
|
|||
func (this *BaseController) checkLogin(writer http.ResponseWriter, request *http.Request) (*Session, *User) { |
|||
|
|||
//验证用户是否已经登录。
|
|||
sessionCookie, err := request.Cookie(COOKIE_AUTH_KEY) |
|||
if err != nil { |
|||
panic(ConstWebResult(RESULT_CODE_LOGIN)) |
|||
} |
|||
|
|||
session := this.sessionDao.FindByUuid(sessionCookie.Value) |
|||
if session == nil { |
|||
panic(ConstWebResult(RESULT_CODE_LOGIN)) |
|||
} else { |
|||
if session.ExpireTime.Before(time.Now()) { |
|||
panic(ConstWebResult(RESULT_CODE_LOGIN_EXPIRED)) |
|||
} else { |
|||
|
|||
user := this.userDao.FindByUuid(session.UserUuid) |
|||
if user == nil { |
|||
panic(ConstWebResult(RESULT_CODE_LOGIN_INVALID)) |
|||
} else { |
|||
return session, user |
|||
} |
|||
|
|||
} |
|||
} |
|||
|
|||
} |
|||
|
|||
func (this *BaseController) checkUser(writer http.ResponseWriter, request *http.Request) *User { |
|||
|
|||
_, user := this.checkLogin(writer, request) |
|||
return user |
|||
|
|||
} |
|||
|
|||
//允许跨域请求
|
|||
func (this *BaseController) allowCORS(writer http.ResponseWriter) { |
|||
writer.Header().Add("Access-Control-Allow-Origin", "*") |
|||
writer.Header().Add("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE") |
|||
writer.Header().Add("Access-Control-Max-Age", "3600") |
|||
} |
|||
@ -0,0 +1,12 @@ |
|||
package rest |
|||
|
|||
import ( |
|||
_ "github.com/jinzhu/gorm/dialects/mysql" |
|||
) |
|||
|
|||
type BaseDao struct { |
|||
Bean |
|||
} |
|||
|
|||
|
|||
|
|||
@ -0,0 +1,48 @@ |
|||
package rest |
|||
|
|||
import ( |
|||
"time" |
|||
"reflect" |
|||
"math" |
|||
) |
|||
|
|||
type Time time.Time |
|||
|
|||
type Base struct { |
|||
Uuid string `gorm:"primary_key" json:"uuid"` |
|||
Sort int64 `json:"sort"` |
|||
ModifyTime time.Time `json:"modifyTime"` |
|||
CreateTime time.Time `json:"createTime"` |
|||
} |
|||
|
|||
//将 Struct 转换成map[string]interface{}类型
|
|||
func (this *Base) Map() map[string]interface{} { |
|||
t := reflect.TypeOf(this) |
|||
v := reflect.ValueOf(this) |
|||
|
|||
var data = make(map[string]interface{}) |
|||
for i := 0; i < t.NumField(); i++ { |
|||
data[t.Field(i).Name] = v.Field(i).Interface() |
|||
} |
|||
return data |
|||
} |
|||
|
|||
//分页类
|
|||
type Pager struct { |
|||
Page int `json:"page"` |
|||
PageSize int `json:"pageSize"` |
|||
TotalItems int `json:"totalItems"` |
|||
TotalPages int `json:"totalPages"` |
|||
Data interface{} `json:"data"` |
|||
} |
|||
|
|||
func NewPager(page int, pageSize int, totalItems int, data interface{}) *Pager { |
|||
|
|||
return &Pager{ |
|||
Page: page, |
|||
PageSize: pageSize, |
|||
TotalItems: totalItems, |
|||
TotalPages: int(math.Ceil(float64(totalItems) / float64(pageSize))), |
|||
Data: data, |
|||
} |
|||
} |
|||
@ -0,0 +1,21 @@ |
|||
package rest |
|||
|
|||
type IBean interface { |
|||
Init(context *Context) |
|||
PanicError(err error); |
|||
} |
|||
|
|||
type Bean struct { |
|||
context *Context |
|||
} |
|||
|
|||
func (this *Bean) Init(context *Context) { |
|||
this.context = context |
|||
} |
|||
|
|||
//处理错误的统一方法
|
|||
func (this *Bean) PanicError(err error) { |
|||
if err != nil { |
|||
panic(err) |
|||
} |
|||
} |
|||
@ -0,0 +1,222 @@ |
|||
package rest |
|||
|
|||
import ( |
|||
"fmt" |
|||
"github.com/json-iterator/go" |
|||
"time" |
|||
"unsafe" |
|||
"io/ioutil" |
|||
"encoding/json" |
|||
"flag" |
|||
) |
|||
|
|||
const ( |
|||
//用户身份的cookie字段名
|
|||
COOKIE_AUTH_KEY = "_ak" |
|||
|
|||
//数据库表前缀 tank100表示当前应用版本是tank:1.0.x版,数据库结构发生变化必然是中型升级
|
|||
TABLE_PREFIX = "tank10_" |
|||
|
|||
//当前版本
|
|||
VERSION = "1.0.0" |
|||
) |
|||
|
|||
var ( |
|||
CONFIG = &Config{ |
|||
//以下内容是默认配置项。
|
|||
|
|||
//默认监听端口号
|
|||
ServerPort: 9090, |
|||
//将日志输出到控制台。
|
|||
LogToConsole: true, |
|||
//mysql相关配置。
|
|||
//数据库端口
|
|||
MysqlPort: 3306, |
|||
//数据库Host
|
|||
MysqlHost: "127.0.0.1", |
|||
//数据库名字
|
|||
MysqlSchema: "tank", |
|||
//用户名
|
|||
MysqlUserName: "tank", |
|||
//密码
|
|||
MysqlPassword: "tank123", |
|||
//数据库连接信息。
|
|||
MysqlUrl: "%MysqlUserName:%MysqlPassword@tcp(%MysqlHost:%MysqlPort)/%MysqlSchema?charset=utf8&parseTime=True&loc=Local", |
|||
//超级管理员用户名,只能包含英文和数字
|
|||
AdminUsername: "admin", |
|||
//超级管理员邮箱
|
|||
AdminEmail: "admin@tank.eyeblue.cn", |
|||
//超级管理员密码
|
|||
AdminPassword: "123456", |
|||
} |
|||
) |
|||
|
|||
//依赖外部定义的变量。
|
|||
type Config struct { |
|||
//默认监听端口号
|
|||
ServerPort int |
|||
|
|||
//将日志输出到控制台。
|
|||
LogToConsole bool |
|||
|
|||
//mysql相关配置。
|
|||
//数据库端口
|
|||
MysqlPort int |
|||
//数据库Host
|
|||
MysqlHost string |
|||
//数据库名字
|
|||
MysqlSchema string |
|||
//用户名
|
|||
MysqlUserName string |
|||
//密码
|
|||
MysqlPassword string |
|||
//数据库连接信息。
|
|||
MysqlUrl string |
|||
|
|||
//超级管理员用户名,只能包含英文和数字
|
|||
AdminUsername string |
|||
//超级管理员邮箱
|
|||
AdminEmail string |
|||
//超级管理员密码
|
|||
AdminPassword string |
|||
} |
|||
|
|||
//验证配置文件的正确性。
|
|||
func (this *Config) validate() { |
|||
|
|||
if this.ServerPort == 0 { |
|||
LogPanic("ServerPort 未配置") |
|||
} |
|||
|
|||
if this.MysqlUserName == "" { |
|||
LogPanic("MysqlUserName 未配置") |
|||
} |
|||
|
|||
if this.MysqlPassword == "" { |
|||
LogPanic("MysqlPassword 未配置") |
|||
} |
|||
|
|||
if this.MysqlHost == "" { |
|||
LogPanic("MysqlHost 未配置") |
|||
} |
|||
|
|||
if this.MysqlPort == 0 { |
|||
LogPanic("MysqlPort 未配置") |
|||
} |
|||
|
|||
if this.MysqlSchema == "" { |
|||
LogPanic("MysqlSchema 未配置") |
|||
} |
|||
|
|||
this.MysqlUrl = fmt.Sprintf("%s:%s@tcp(%s:%d)/%s?charset=utf8&parseTime=True&loc=Local", this.MysqlUserName, this.MysqlPassword, this.MysqlHost, this.MysqlPort, this.MysqlSchema) |
|||
|
|||
} |
|||
|
|||
//init方法只要这个包被引入了就一定会执行。
|
|||
func init() { |
|||
|
|||
//json中需要去特殊处理时间。
|
|||
jsoniter.RegisterTypeDecoderFunc("time.Time", func(ptr unsafe.Pointer, iter *jsoniter.Iterator) { |
|||
//如果使用time.UTC,那么时间会相差8小时
|
|||
t, err := time.ParseInLocation("2006-01-02 15:04:05", iter.ReadString(), time.Local) |
|||
if err != nil { |
|||
iter.Error = err |
|||
return |
|||
} |
|||
*((*time.Time)(ptr)) = t |
|||
}) |
|||
|
|||
jsoniter.RegisterTypeEncoderFunc("time.Time", func(ptr unsafe.Pointer, stream *jsoniter.Stream) { |
|||
t := *((*time.Time)(ptr)) |
|||
//如果使用time.UTC,那么时间会相差8小时
|
|||
stream.WriteString(t.Local().Format("2006-01-02 15:04:05")) |
|||
}, nil) |
|||
} |
|||
|
|||
//从conf/tank.json中获取变量。
|
|||
|
|||
//从flag中或者conf/tank.json中装填变量
|
|||
func PrepareConfigs() { |
|||
|
|||
//读取配置文件
|
|||
filePath := GetConfPath() + "/tank.json" |
|||
content, err := ioutil.ReadFile(filePath) |
|||
if err != nil { |
|||
LogWarning(fmt.Sprintf("无法找到配置文件:%s,%v", filePath, err)) |
|||
} else { |
|||
// 用 json.Unmarshal
|
|||
err := json.Unmarshal(content, CONFIG) |
|||
if err != nil { |
|||
LogPanic("配置文件格式错误!") |
|||
} |
|||
} |
|||
|
|||
//从运行时参数中读取,运行时参数具有更高优先级。
|
|||
//系统端口号
|
|||
ServerPortPtr := flag.Int("ServerPort", CONFIG.ServerPort, "server port") |
|||
|
|||
//系统端口号
|
|||
LogToConsolePtr := flag.Bool("LogToConsole", CONFIG.LogToConsole, "write log to console. for debug.") |
|||
|
|||
//mysql相关配置。
|
|||
MysqlPortPtr := flag.Int("MysqlPort", CONFIG.MysqlPort, "mysql port") |
|||
MysqlHostPtr := flag.String("MysqlHost", CONFIG.MysqlHost, "mysql host") |
|||
MysqlSchemaPtr := flag.String("MysqlSchema", CONFIG.MysqlSchema, "mysql schema") |
|||
MysqlUserNamePtr := flag.String("MysqlUserName", CONFIG.MysqlUserName, "mysql username") |
|||
MysqlPasswordPtr := flag.String("MysqlPassword", CONFIG.MysqlPassword, "mysql password") |
|||
|
|||
//超级管理员信息
|
|||
AdminUsernamePtr := flag.String("AdminUsername", CONFIG.AdminUsername, "administrator username") |
|||
AdminEmailPtr := flag.String("AdminEmail", CONFIG.AdminEmail, "administrator email") |
|||
AdminPasswordPtr := flag.String("AdminPassword", CONFIG.AdminPassword, "administrator password") |
|||
|
|||
//flag.Parse()方法必须要在使用之前调用。
|
|||
flag.Parse() |
|||
|
|||
if *ServerPortPtr != CONFIG.ServerPort { |
|||
CONFIG.ServerPort = *ServerPortPtr |
|||
} |
|||
|
|||
if *LogToConsolePtr != CONFIG.LogToConsole { |
|||
CONFIG.LogToConsole = *LogToConsolePtr |
|||
} |
|||
|
|||
if *MysqlPortPtr != CONFIG.MysqlPort { |
|||
CONFIG.MysqlPort = *MysqlPortPtr |
|||
} |
|||
|
|||
if *MysqlHostPtr != CONFIG.MysqlHost { |
|||
CONFIG.MysqlHost = *MysqlHostPtr |
|||
} |
|||
|
|||
if *MysqlSchemaPtr != CONFIG.MysqlSchema { |
|||
CONFIG.MysqlSchema = *MysqlSchemaPtr |
|||
} |
|||
|
|||
if *MysqlUserNamePtr != CONFIG.MysqlUserName { |
|||
CONFIG.MysqlUserName = *MysqlUserNamePtr |
|||
} |
|||
|
|||
if *MysqlPasswordPtr != CONFIG.MysqlPassword { |
|||
CONFIG.MysqlPassword = *MysqlPasswordPtr |
|||
} |
|||
|
|||
if *AdminUsernamePtr != CONFIG.AdminUsername { |
|||
CONFIG.AdminUsername = *AdminUsernamePtr |
|||
} |
|||
|
|||
if *AdminEmailPtr != CONFIG.AdminEmail { |
|||
CONFIG.AdminEmail = *AdminEmailPtr |
|||
} |
|||
|
|||
if *AdminPasswordPtr != CONFIG.AdminPassword { |
|||
CONFIG.AdminPassword = *AdminPasswordPtr |
|||
} |
|||
|
|||
//验证配置项的正确性
|
|||
CONFIG.validate() |
|||
|
|||
//安装程序开始导入初始表和初始数据。
|
|||
InstallDatabase() |
|||
|
|||
} |
|||
@ -0,0 +1,146 @@ |
|||
package rest |
|||
|
|||
import ( |
|||
"fmt" |
|||
"github.com/jinzhu/gorm" |
|||
"reflect" |
|||
) |
|||
|
|||
//上下文,管理数据库连接,管理所有路由请求,管理所有的单例component.
|
|||
type Context struct { |
|||
//数据库连接
|
|||
DB *gorm.DB |
|||
//处理所有路由请求
|
|||
Router *Router |
|||
//各类的Bean Map。这里面是包含ControllerMap中所有元素
|
|||
BeanMap map[string]IBean |
|||
//只包含了Controller的map
|
|||
ControllerMap map[string]IController |
|||
} |
|||
|
|||
func (this *Context) OpenDb() { |
|||
|
|||
var err error = nil |
|||
this.DB, err = gorm.Open("mysql", CONFIG.MysqlUrl) |
|||
|
|||
//是否打开sql日志
|
|||
this.DB.LogMode(false) |
|||
if err != nil { |
|||
panic("failed to connect mysql database") |
|||
} |
|||
} |
|||
|
|||
func (this *Context) CloseDb() { |
|||
|
|||
if this.DB != nil { |
|||
this.DB.Close() |
|||
} |
|||
} |
|||
|
|||
//构造方法
|
|||
func NewContext() *Context { |
|||
|
|||
context := &Context{} |
|||
|
|||
//处理数据库连接的开关。
|
|||
context.OpenDb() |
|||
|
|||
//初始化Map
|
|||
context.BeanMap = make(map[string]IBean) |
|||
context.ControllerMap = make(map[string]IController) |
|||
|
|||
//注册各类Beans.在这个方法里面顺便把Controller装入ControllerMap中去。
|
|||
context.registerBeans() |
|||
|
|||
//初始化每个bean.
|
|||
context.initBeans() |
|||
|
|||
//初始化Router. 这个方法要在Bean注册好了之后才能。
|
|||
context.Router = NewRouter(context) |
|||
|
|||
return context |
|||
|
|||
} |
|||
|
|||
//注册一个Bean
|
|||
func (this *Context) registerBean(bean IBean) { |
|||
|
|||
typeOf := reflect.TypeOf(bean) |
|||
typeName := typeOf.String() |
|||
|
|||
if element, ok := bean.(IBean); ok { |
|||
|
|||
err := fmt.Sprintf("【%s】已经被注册了,跳过。", typeName) |
|||
if _, ok := this.BeanMap[typeName]; ok { |
|||
LogError(fmt.Sprintf(err)) |
|||
} else { |
|||
this.BeanMap[typeName] = element |
|||
|
|||
//看看是不是controller类型,如果是,那么单独放在ControllerMap中。
|
|||
if controller, ok1 := bean.(IController); ok1 { |
|||
this.ControllerMap[typeName] = controller |
|||
} |
|||
|
|||
} |
|||
|
|||
} else { |
|||
err := fmt.Sprintf("注册的【%s】不是Bean类型。", typeName) |
|||
panic(err) |
|||
} |
|||
|
|||
} |
|||
|
|||
//注册各个Beans
|
|||
func (this *Context) registerBeans() { |
|||
|
|||
//alien
|
|||
this.registerBean(new(AlienController)) |
|||
|
|||
//downloadToken
|
|||
this.registerBean(new(DownloadTokenDao)) |
|||
|
|||
//matter
|
|||
this.registerBean(new(MatterController)) |
|||
this.registerBean(new(MatterDao)) |
|||
this.registerBean(new(MatterService)) |
|||
|
|||
//session
|
|||
this.registerBean(new(SessionDao)) |
|||
|
|||
//uploadToken
|
|||
this.registerBean(new(UploadTokenDao)) |
|||
|
|||
//user
|
|||
this.registerBean(new(UserController)) |
|||
this.registerBean(new(UserDao)) |
|||
|
|||
} |
|||
|
|||
//从Map中获取某个Bean.
|
|||
func (this *Context) GetBean(bean IBean) IBean { |
|||
|
|||
typeOf := reflect.TypeOf(bean) |
|||
typeName := typeOf.String() |
|||
|
|||
if val, ok := this.BeanMap[typeName]; ok { |
|||
return val |
|||
} else { |
|||
err := fmt.Sprintf("【%s】没有注册。", typeName) |
|||
panic(err) |
|||
} |
|||
} |
|||
|
|||
//初始化每个Bean
|
|||
func (this *Context) initBeans() { |
|||
|
|||
for _, bean := range this.BeanMap { |
|||
bean.Init(this) |
|||
} |
|||
|
|||
} |
|||
|
|||
//销毁的方法
|
|||
func (this *Context) Destroy() { |
|||
this.CloseDb() |
|||
|
|||
} |
|||
@ -0,0 +1,60 @@ |
|||
package rest |
|||
|
|||
import ( |
|||
_ "github.com/jinzhu/gorm/dialects/mysql" |
|||
"github.com/nu7hatch/gouuid" |
|||
"time" |
|||
) |
|||
|
|||
type DownloadTokenDao struct { |
|||
BaseDao |
|||
} |
|||
|
|||
//按照Id查询
|
|||
func (this *DownloadTokenDao) FindByUuid(uuid string) *DownloadToken { |
|||
|
|||
// Read
|
|||
var downloadToken = &DownloadToken{} |
|||
db := this.context.DB.Where(&DownloadToken{Base: Base{Uuid: uuid}}).First(downloadToken) |
|||
if db.Error != nil { |
|||
return nil |
|||
} |
|||
return downloadToken |
|||
|
|||
} |
|||
|
|||
//按照Id查询
|
|||
func (this *DownloadTokenDao) CheckByUuid(uuid string) *DownloadToken { |
|||
|
|||
// Read
|
|||
var downloadToken = &DownloadToken{} |
|||
db := this.context.DB.Where(&DownloadToken{Base: Base{Uuid: uuid}}).First(downloadToken) |
|||
this.PanicError(db.Error) |
|||
return downloadToken |
|||
|
|||
} |
|||
|
|||
//创建一个session并且持久化到数据库中。
|
|||
func (this *DownloadTokenDao) Create(downloadToken *DownloadToken) *DownloadToken { |
|||
|
|||
timeUUID, _ := uuid.NewV4() |
|||
downloadToken.Uuid = string(timeUUID.String()) |
|||
|
|||
downloadToken.CreateTime = time.Now() |
|||
downloadToken.ModifyTime = time.Now() |
|||
|
|||
db := this.context.DB.Create(downloadToken) |
|||
this.PanicError(db.Error) |
|||
|
|||
return downloadToken |
|||
} |
|||
|
|||
//修改一个downloadToken
|
|||
func (this *DownloadTokenDao) Save(downloadToken *DownloadToken) *DownloadToken { |
|||
|
|||
downloadToken.ModifyTime = time.Now() |
|||
db := this.context.DB.Save(downloadToken) |
|||
this.PanicError(db.Error) |
|||
|
|||
return downloadToken |
|||
} |
|||
@ -0,0 +1,17 @@ |
|||
package rest |
|||
|
|||
import ( |
|||
"time" |
|||
) |
|||
|
|||
type DownloadToken struct { |
|||
Base |
|||
UserUuid string `json:"userUuid"` |
|||
MatterUuid string `json:"matterUuid"` |
|||
ExpireTime time.Time `json:"expireTime"` |
|||
Ip string `json:"ip"` |
|||
} |
|||
|
|||
func (DownloadToken) TableName() string { |
|||
return TABLE_PREFIX + "download_token" |
|||
} |
|||
@ -0,0 +1,117 @@ |
|||
package rest |
|||
|
|||
import ( |
|||
"fmt" |
|||
"github.com/jinzhu/gorm" |
|||
"github.com/nu7hatch/gouuid" |
|||
|
|||
"time" |
|||
"regexp" |
|||
) |
|||
|
|||
//首次运行的时候,将自动安装数据库等内容。
|
|||
func InstallDatabase() { |
|||
|
|||
db, err := gorm.Open("mysql", CONFIG.MysqlUrl) |
|||
if err != nil { |
|||
LogPanic(fmt.Sprintf("无法打开%s", CONFIG.MysqlUrl)) |
|||
} |
|||
if db != nil { |
|||
defer db.Close() |
|||
} |
|||
|
|||
//这个方法只会简单查看表是否存在,不会去比照每个字段的。因此如果用户自己修改表结构将会出现不可预测的错误。
|
|||
var hasTable = true |
|||
downloadToken := &DownloadToken{} |
|||
hasTable = db.HasTable(downloadToken) |
|||
if !hasTable { |
|||
|
|||
createDownloadToken := "CREATE TABLE `tank10_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,`modify_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表';" |
|||
db = db.Exec(createDownloadToken) |
|||
if db.Error != nil { |
|||
LogPanic(db.Error) |
|||
} |
|||
LogInfo("创建DownloadToken表") |
|||
|
|||
} |
|||
|
|||
matter := &Matter{} |
|||
hasTable = db.HasTable(matter) |
|||
if !hasTable { |
|||
createMatter := "CREATE TABLE `tank10_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 NULL COMMENT '是否是文件夹',`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,`modify_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表';" |
|||
db = db.Exec(createMatter) |
|||
if db.Error != nil { |
|||
LogPanic(db.Error) |
|||
} |
|||
LogInfo("创建Matter表") |
|||
|
|||
} |
|||
session := &Session{} |
|||
hasTable = db.HasTable(session) |
|||
if !hasTable { |
|||
|
|||
createSession := "CREATE TABLE `tank10_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,`modify_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表';" |
|||
db = db.Exec(createSession) |
|||
if db.Error != nil { |
|||
LogPanic(db.Error) |
|||
} |
|||
LogInfo("创建Session表") |
|||
} |
|||
|
|||
uploadToken := &UploadToken{} |
|||
hasTable = db.HasTable(uploadToken) |
|||
if !hasTable { |
|||
|
|||
createUploadToken := "CREATE TABLE `tank10_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,`modify_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表';" |
|||
db = db.Exec(createUploadToken) |
|||
if db.Error != nil { |
|||
LogPanic(db.Error) |
|||
} |
|||
LogInfo("创建UploadToken表") |
|||
} |
|||
|
|||
user := &User{} |
|||
hasTable = db.HasTable(user) |
|||
if !hasTable { |
|||
|
|||
//验证超级管理员的信息
|
|||
if m, _ := regexp.MatchString(`^[0-9a-zA-Z_]+$`, CONFIG.AdminUsername); !m { |
|||
LogPanic(`超级管理员用户名必填,且只能包含字母,数字和'_''`) |
|||
} |
|||
|
|||
if len(CONFIG.AdminPassword) < 6 { |
|||
LogPanic(`超级管理员密码长度至少为6位`) |
|||
} |
|||
|
|||
if CONFIG.AdminEmail == "" { |
|||
LogPanic("超级管理员邮箱必填!") |
|||
} |
|||
|
|||
|
|||
createUser := "CREATE TABLE `tank10_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,`status` varchar(45) DEFAULT 'OK',`sort` bigint(20) DEFAULT NULL,`modify_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='用户表描述';" |
|||
db = db.Exec(createUser) |
|||
if db.Error != nil { |
|||
LogPanic(db.Error) |
|||
} |
|||
LogInfo("创建User表") |
|||
|
|||
user := &User{} |
|||
timeUUID, _ := uuid.NewV4() |
|||
user.Uuid = string(timeUUID.String()) |
|||
user.CreateTime = time.Now() |
|||
user.ModifyTime = time.Now() |
|||
user.LastTime = time.Now() |
|||
user.Sort = time.Now().UnixNano() / 1e6 |
|||
user.Role = USER_ROLE_ADMINISTRATOR |
|||
user.Username = CONFIG.AdminUsername |
|||
user.Password = GetBcrypt(CONFIG.AdminPassword) |
|||
user.Email = CONFIG.AdminEmail |
|||
user.Phone = "" |
|||
user.Gender = USER_GENDER_UNKNOWN |
|||
user.Status = USER_STATUS_OK |
|||
|
|||
db.Create(user) |
|||
|
|||
} |
|||
|
|||
} |
|||
@ -0,0 +1,384 @@ |
|||
package rest |
|||
|
|||
import ( |
|||
"net/http" |
|||
"regexp" |
|||
"strconv" |
|||
"strings" |
|||
) |
|||
|
|||
type MatterController struct { |
|||
BaseController |
|||
matterDao *MatterDao |
|||
matterService *MatterService |
|||
downloadTokenDao *DownloadTokenDao |
|||
} |
|||
|
|||
//初始化方法
|
|||
func (this *MatterController) Init(context *Context) { |
|||
this.BaseController.Init(context) |
|||
|
|||
//手动装填本实例的Bean. 这里必须要用中间变量方可。
|
|||
b := context.GetBean(this.matterDao) |
|||
if b, ok := b.(*MatterDao); ok { |
|||
this.matterDao = b |
|||
} |
|||
|
|||
b = context.GetBean(this.matterService) |
|||
if b, ok := b.(*MatterService); ok { |
|||
this.matterService = b |
|||
} |
|||
|
|||
b = context.GetBean(this.downloadTokenDao) |
|||
if b, ok := b.(*DownloadTokenDao); ok { |
|||
this.downloadTokenDao = b |
|||
} |
|||
|
|||
} |
|||
|
|||
//注册自己的路由。
|
|||
func (this *MatterController) RegisterRoutes() map[string]func(writer http.ResponseWriter, request *http.Request) { |
|||
|
|||
routeMap := make(map[string]func(writer http.ResponseWriter, request *http.Request)) |
|||
|
|||
//每个Controller需要主动注册自己的路由。
|
|||
routeMap["/api/matter/create/directory"] = this.Wrap(this.CreateDirectory, USER_ROLE_USER) |
|||
routeMap["/api/matter/upload"] = this.Wrap(this.Upload, USER_ROLE_USER) |
|||
routeMap["/api/matter/delete"] = this.Wrap(this.Delete, USER_ROLE_USER) |
|||
routeMap["/api/matter/delete/batch"] = this.Wrap(this.DeleteBatch, USER_ROLE_USER) |
|||
routeMap["/api/matter/rename"] = this.Wrap(this.Rename, USER_ROLE_USER) |
|||
routeMap["/api/matter/move"] = this.Wrap(this.Move, USER_ROLE_USER) |
|||
routeMap["/api/matter/detail"] = this.Wrap(this.Detail, USER_ROLE_USER) |
|||
routeMap["/api/matter/page"] = this.Wrap(this.Page, USER_ROLE_USER) |
|||
|
|||
return routeMap |
|||
} |
|||
|
|||
//查看某个文件的详情。
|
|||
func (this *MatterController) Detail(writer http.ResponseWriter, request *http.Request) *WebResult { |
|||
|
|||
uuid := request.FormValue("uuid") |
|||
if uuid == "" { |
|||
return this.Error("文件的uuid必填") |
|||
} |
|||
|
|||
matter := this.matterDao.FindByUuid(uuid) |
|||
|
|||
//组装file的内容,展示其父组件。
|
|||
puuid := matter.Puuid |
|||
tmpMatter := matter |
|||
for puuid != "root" { |
|||
pFile := this.matterDao.FindByUuid(puuid) |
|||
|
|||
tmpMatter.Parent = pFile |
|||
tmpMatter = pFile |
|||
puuid = pFile.Puuid |
|||
|
|||
} |
|||
|
|||
return this.Success(matter) |
|||
|
|||
} |
|||
|
|||
//创建一个文件夹。
|
|||
func (this *MatterController) CreateDirectory(writer http.ResponseWriter, request *http.Request) *WebResult { |
|||
|
|||
puuid := request.FormValue("puuid") |
|||
name := request.FormValue("name") |
|||
|
|||
userUuid := request.FormValue("userUuid") |
|||
user := this.checkUser(writer, request) |
|||
if user.Role != USER_ROLE_ADMINISTRATOR { |
|||
userUuid = user.Uuid |
|||
} |
|||
user = this.userDao.CheckByUuid(userUuid) |
|||
|
|||
//验证参数。
|
|||
if name == "" { |
|||
return this.Error("name参数必填") |
|||
} |
|||
if m, _ := regexp.MatchString(`[<>|*?/\\]`, name); m { |
|||
return this.Error(`名称中不能包含以下特殊符号:< > | * ? / \`) |
|||
} |
|||
|
|||
if puuid != "" && puuid != "root" { |
|||
//找出上一级的文件夹。
|
|||
this.matterDao.FindByUuidAndUserUuid(puuid, user.Uuid) |
|||
} |
|||
|
|||
//判断同级文件夹中是否有同名的文件。
|
|||
count := this.matterDao.CountByUserUuidAndPuuidAndDirAndName(user.Uuid, puuid, true, name) |
|||
|
|||
if count > 0 { |
|||
return this.Error("【" + name + "】已经存在了,请使用其他名称。") |
|||
} |
|||
|
|||
matter := &Matter{ |
|||
Puuid: puuid, |
|||
UserUuid: user.Uuid, |
|||
Dir: true, |
|||
Name: name, |
|||
} |
|||
|
|||
matter = this.matterDao.Create(matter) |
|||
|
|||
return this.Success(matter) |
|||
} |
|||
|
|||
//按照分页的方式获取某个文件夹下文件和子文件夹的列表,通常情况下只有一页。
|
|||
func (this *MatterController) Page(writer http.ResponseWriter, request *http.Request) *WebResult { |
|||
|
|||
//如果是根目录,那么就传入root.
|
|||
puuid := request.FormValue("puuid") |
|||
pageStr := request.FormValue("page") |
|||
pageSizeStr := request.FormValue("pageSize") |
|||
userUuid := request.FormValue("userUuid") |
|||
name := request.FormValue("name") |
|||
dir := request.FormValue("dir") |
|||
orderDir := request.FormValue("orderDir") |
|||
orderCreateTime := request.FormValue("orderCreateTime") |
|||
orderSize := request.FormValue("orderSize") |
|||
orderName := request.FormValue("orderName") |
|||
extensionsStr := request.FormValue("extensions") |
|||
|
|||
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 |
|||
} |
|||
} |
|||
|
|||
//筛选后缀名
|
|||
var extensions []string |
|||
if extensionsStr != "" { |
|||
extensions = strings.Split(extensionsStr, ",") |
|||
} |
|||
|
|||
//文件列表默认文件夹始终在文件的前面。
|
|||
if orderDir == "" { |
|||
orderDir = "DESC" |
|||
} |
|||
|
|||
sortArray := []OrderPair{ |
|||
{ |
|||
key: "dir", |
|||
value: orderDir, |
|||
}, |
|||
{ |
|||
key: "create_time", |
|||
value: orderCreateTime, |
|||
}, |
|||
{ |
|||
key: "size", |
|||
value: orderSize, |
|||
}, |
|||
{ |
|||
key: "name", |
|||
value: orderName, |
|||
}, |
|||
} |
|||
|
|||
pager := this.matterDao.Page(page, pageSize, puuid, userUuid, name, dir, extensions, sortArray) |
|||
|
|||
return this.Success(pager) |
|||
} |
|||
|
|||
//上传文件
|
|||
func (this *MatterController) Upload(writer http.ResponseWriter, request *http.Request) *WebResult { |
|||
|
|||
userUuid := request.FormValue("userUuid") |
|||
user := this.checkUser(writer, request) |
|||
if user.Role != USER_ROLE_ADMINISTRATOR { |
|||
userUuid = user.Uuid |
|||
} |
|||
user = this.userDao.CheckByUuid(userUuid) |
|||
|
|||
puuid := request.FormValue("puuid") |
|||
if puuid == "" { |
|||
return this.Error("puuid必填") |
|||
} else { |
|||
if puuid != "root" { |
|||
//找出上一级的文件夹。
|
|||
this.matterDao.FindByUuidAndUserUuid(puuid, userUuid) |
|||
|
|||
} |
|||
|
|||
} |
|||
|
|||
request.ParseMultipartForm(32 << 20) |
|||
file, handler, err := request.FormFile("file") |
|||
this.PanicError(err) |
|||
defer file.Close() |
|||
|
|||
matter := this.matterService.Upload(file, user, puuid, handler.Filename, true) |
|||
|
|||
return this.Success(matter) |
|||
} |
|||
|
|||
//删除一个文件
|
|||
func (this *MatterController) Delete(writer http.ResponseWriter, request *http.Request) *WebResult { |
|||
|
|||
uuid := request.FormValue("uuid") |
|||
if uuid == "" { |
|||
return this.Error("文件的uuid必填") |
|||
} |
|||
|
|||
matter := this.matterDao.FindByUuid(uuid) |
|||
|
|||
//判断文件的所属人是否正确
|
|||
user := this.checkUser(writer, request) |
|||
if user.Role != USER_ROLE_ADMINISTRATOR && matter.UserUuid != user.Uuid { |
|||
return this.Error(RESULT_CODE_UNAUTHORIZED) |
|||
} |
|||
|
|||
this.matterDao.Delete(matter) |
|||
|
|||
return this.Success("删除成功!") |
|||
} |
|||
|
|||
//删除一系列文件。
|
|||
func (this *MatterController) DeleteBatch(writer http.ResponseWriter, request *http.Request) *WebResult { |
|||
|
|||
uuids := request.FormValue("uuids") |
|||
if uuids == "" { |
|||
return this.Error("文件的uuids必填") |
|||
} |
|||
|
|||
uuidArray := strings.Split(uuids, ",") |
|||
|
|||
for _, uuid := range uuidArray { |
|||
|
|||
matter := this.matterDao.FindByUuid(uuid) |
|||
|
|||
//判断文件的所属人是否正确
|
|||
user := this.checkUser(writer, request) |
|||
if user.Role != USER_ROLE_ADMINISTRATOR && matter.UserUuid != user.Uuid { |
|||
return this.Error(RESULT_CODE_UNAUTHORIZED) |
|||
} |
|||
|
|||
this.matterDao.Delete(matter) |
|||
|
|||
} |
|||
|
|||
return this.Success("删除成功!") |
|||
} |
|||
|
|||
//重命名一个文件或一个文件夹
|
|||
func (this *MatterController) Rename(writer http.ResponseWriter, request *http.Request) *WebResult { |
|||
|
|||
uuid := request.FormValue("uuid") |
|||
name := request.FormValue("name") |
|||
|
|||
//验证参数。
|
|||
if name == "" { |
|||
return this.Error("name参数必填") |
|||
} |
|||
if m, _ := regexp.MatchString(`[<>|*?/\\]`, name); m { |
|||
return this.Error(`名称中不能包含以下特殊符号:< > | * ? / \`) |
|||
} |
|||
|
|||
//找出该文件或者文件夹
|
|||
matter := this.matterDao.FindByUuid(uuid) |
|||
|
|||
user := this.checkUser(writer, request) |
|||
if user.Role != USER_ROLE_ADMINISTRATOR && matter.UserUuid != user.Uuid { |
|||
return this.Error(RESULT_CODE_UNAUTHORIZED) |
|||
} |
|||
|
|||
if name == matter.Name { |
|||
return this.Error("新名称和旧名称一样,操作失败!") |
|||
} |
|||
|
|||
//判断同级文件夹中是否有同名的文件
|
|||
count := this.matterDao.CountByUserUuidAndPuuidAndDirAndName(user.Uuid, matter.Puuid, matter.Dir, name) |
|||
|
|||
if count > 0 { |
|||
return this.Error("【" + name + "】已经存在了,请使用其他名称。") |
|||
} |
|||
|
|||
matter.Name = name |
|||
matter = this.matterDao.Save(matter) |
|||
|
|||
return this.Success(matter) |
|||
} |
|||
|
|||
//将一个文件夹或者文件移入到另一个文件夹下。
|
|||
func (this *MatterController) Move(writer http.ResponseWriter, request *http.Request) *WebResult { |
|||
|
|||
srcUuidsStr := request.FormValue("srcUuids") |
|||
destUuid := request.FormValue("destUuid") |
|||
|
|||
var srcUuids []string |
|||
//验证参数。
|
|||
if srcUuidsStr == "" { |
|||
return this.Error("srcUuids参数必填") |
|||
} else { |
|||
srcUuids = strings.Split(srcUuidsStr, ",") |
|||
} |
|||
|
|||
userUuid := request.FormValue("userUuid") |
|||
user := this.checkUser(writer, request) |
|||
if user.Role != USER_ROLE_ADMINISTRATOR { |
|||
userUuid = user.Uuid |
|||
} |
|||
if userUuid == "" { |
|||
userUuid = user.Uuid |
|||
} |
|||
|
|||
user = this.userDao.CheckByUuid(userUuid) |
|||
|
|||
//验证dest是否有问题
|
|||
if destUuid == "" { |
|||
return this.Error("destUuid参数必填") |
|||
} else { |
|||
if destUuid != "root" { |
|||
destMatter := this.matterDao.FindByUuid(destUuid) |
|||
|
|||
if user.Role != USER_ROLE_ADMINISTRATOR && destMatter.UserUuid != user.Uuid { |
|||
return this.Error(RESULT_CODE_UNAUTHORIZED) |
|||
} |
|||
} |
|||
} |
|||
|
|||
var srcMatters []*Matter |
|||
//验证src是否有问题。
|
|||
for _, uuid := range srcUuids { |
|||
//找出该文件或者文件夹
|
|||
srcMatter := this.matterDao.FindByUuid(uuid) |
|||
|
|||
if user.Role != USER_ROLE_ADMINISTRATOR && srcMatter.UserUuid != user.Uuid { |
|||
return this.Error(RESULT_CODE_UNAUTHORIZED) |
|||
} |
|||
|
|||
if srcMatter.Puuid == destUuid { |
|||
return this.Error("没有进行移动,操作无效!") |
|||
} |
|||
|
|||
//判断同级文件夹中是否有同名的文件
|
|||
count := this.matterDao.CountByUserUuidAndPuuidAndDirAndName(user.Uuid, destUuid, srcMatter.Dir, srcMatter.Name) |
|||
|
|||
if count > 0 { |
|||
return this.Error("【" + srcMatter.Name + "】在目标文件夹已经存在了,操作失败。") |
|||
} |
|||
|
|||
srcMatters = append(srcMatters, srcMatter) |
|||
} |
|||
|
|||
for _, srcMatter := range srcMatters { |
|||
srcMatter.Puuid = destUuid |
|||
srcMatter = this.matterDao.Save(srcMatter) |
|||
} |
|||
|
|||
return this.Success(nil) |
|||
} |
|||
@ -0,0 +1,237 @@ |
|||
package rest |
|||
|
|||
import ( |
|||
"fmt" |
|||
"github.com/jinzhu/gorm" |
|||
_ "github.com/jinzhu/gorm/dialects/mysql" |
|||
"github.com/nu7hatch/gouuid" |
|||
"os" |
|||
|
|||
"time" |
|||
) |
|||
|
|||
type MatterDao struct { |
|||
BaseDao |
|||
} |
|||
|
|||
//按照Id查询文件
|
|||
func (this *MatterDao) FindByUuid(uuid string) *Matter { |
|||
|
|||
// Read
|
|||
var matter Matter |
|||
db := this.context.DB.Where(&Matter{Base: Base{Uuid: uuid}}).First(&matter) |
|||
if db.Error != nil { |
|||
return nil |
|||
} |
|||
|
|||
return &matter |
|||
|
|||
} |
|||
|
|||
//按照Id查询文件
|
|||
func (this *MatterDao) CheckByUuid(uuid string) *Matter { |
|||
|
|||
// Read
|
|||
var matter Matter |
|||
db := this.context.DB.Where(&Matter{Base: Base{Uuid: uuid}}).First(&matter) |
|||
this.PanicError(db.Error) |
|||
|
|||
return &matter |
|||
|
|||
} |
|||
|
|||
//按照名字查询文件夹
|
|||
func (this *MatterDao) FindByUserUuidAndPuuidAndNameAndDirTrue(userUuid string, puuid string, name string) *Matter { |
|||
|
|||
var wp = &WherePair{} |
|||
|
|||
if userUuid != "" { |
|||
wp = wp.And(&WherePair{Query: "user_uuid = ?", Args: []interface{}{userUuid}}) |
|||
} |
|||
|
|||
if puuid != "" { |
|||
wp = wp.And(&WherePair{Query: "puuid = ?", Args: []interface{}{puuid}}) |
|||
} |
|||
|
|||
if name != "" { |
|||
wp = wp.And(&WherePair{Query: "name = ?", Args: []interface{}{name}}) |
|||
} |
|||
|
|||
wp = wp.And(&WherePair{Query: "dir = ?", Args: []interface{}{1}}) |
|||
|
|||
var matter = &Matter{} |
|||
db := this.context.DB.Model(&Matter{}).Where(wp.Query, wp.Args...).First(matter) |
|||
|
|||
if db.Error != nil { |
|||
return nil |
|||
} |
|||
|
|||
return matter |
|||
} |
|||
|
|||
//按照id和userUuid来查找。
|
|||
func (this *MatterDao) FindByUuidAndUserUuid(uuid string, userUuid string) *Matter { |
|||
|
|||
// Read
|
|||
var matter = &Matter{} |
|||
db := this.context.DB.Where(&Matter{Base: Base{Uuid: uuid}, UserUuid: userUuid}).First(matter) |
|||
this.PanicError(db.Error) |
|||
|
|||
return matter |
|||
|
|||
} |
|||
|
|||
//统计某个用户的某个文件夹下的某个名字的文件(或文件夹)数量。
|
|||
func (this *MatterDao) CountByUserUuidAndPuuidAndDirAndName(userUuid string, puuid string, dir bool, name string) int { |
|||
|
|||
var matter Matter |
|||
var count int |
|||
|
|||
var wp = &WherePair{} |
|||
|
|||
if puuid != "" { |
|||
wp = wp.And(&WherePair{Query: "puuid = ?", Args: []interface{}{puuid}}) |
|||
} |
|||
|
|||
if userUuid != "" { |
|||
wp = wp.And(&WherePair{Query: "user_uuid = ?", Args: []interface{}{userUuid}}) |
|||
} |
|||
|
|||
if name != "" { |
|||
wp = wp.And(&WherePair{Query: "name = ?", Args: []interface{}{name}}) |
|||
} |
|||
|
|||
wp = wp.And(&WherePair{Query: "dir = ?", Args: []interface{}{dir}}) |
|||
|
|||
db := this.context.DB. |
|||
Model(&matter). |
|||
Where(wp.Query, wp.Args...). |
|||
Count(&count) |
|||
this.PanicError(db.Error) |
|||
|
|||
return count |
|||
} |
|||
|
|||
//获取某个用户的某个文件夹下的某个名字的文件(或文件夹)列表
|
|||
func (this *MatterDao) ListByUserUuidAndPuuidAndDirAndName(userUuid string, puuid string, dir bool, name string) []*Matter { |
|||
|
|||
var matters []*Matter |
|||
|
|||
db := this.context.DB. |
|||
Where(Matter{UserUuid: userUuid, Puuid: puuid, Dir: dir, Name: name}). |
|||
Find(&matters) |
|||
this.PanicError(db.Error) |
|||
|
|||
return matters |
|||
} |
|||
|
|||
//获取某个文件夹下所有的文件和子文件
|
|||
func (this *MatterDao) List(puuid string, userUuid string, sortArray []OrderPair) []*Matter { |
|||
var matters []*Matter |
|||
|
|||
db := this.context.DB.Where(Matter{UserUuid: userUuid, Puuid: puuid}).Order(this.GetSortString(sortArray)).Find(&matters) |
|||
this.PanicError(db.Error) |
|||
|
|||
return matters |
|||
} |
|||
|
|||
//获取某个文件夹下所有的文件和子文件
|
|||
func (this *MatterDao) Page(page int, pageSize int, puuid string, userUuid string, name string, dir string, extensions []string, sortArray []OrderPair) *Pager { |
|||
|
|||
var wp = &WherePair{} |
|||
|
|||
if puuid != "" { |
|||
wp = wp.And(&WherePair{Query: "puuid = ?", Args: []interface{}{puuid}}) |
|||
} |
|||
|
|||
if userUuid != "" { |
|||
wp = wp.And(&WherePair{Query: "user_uuid = ?", Args: []interface{}{userUuid}}) |
|||
} |
|||
|
|||
if name != "" { |
|||
wp = wp.And(&WherePair{Query: "name LIKE ?", Args: []interface{}{"%" + name + "%"}}) |
|||
} |
|||
|
|||
if dir == "true" { |
|||
wp = wp.And(&WherePair{Query: "dir = ?", Args: []interface{}{1}}) |
|||
} else if dir == "false" { |
|||
wp = wp.And(&WherePair{Query: "dir = ?", Args: []interface{}{0}}) |
|||
} |
|||
|
|||
var conditionDB *gorm.DB |
|||
if extensions != nil && len(extensions) > 0 { |
|||
var orWp = &WherePair{} |
|||
|
|||
for _, v := range extensions { |
|||
orWp = orWp.Or(&WherePair{Query: "name LIKE ?", Args: []interface{}{"%." + v}}) |
|||
} |
|||
|
|||
conditionDB = this.context.DB.Model(&Matter{}).Where(wp.Query, wp.Args...).Where(orWp.Query, orWp.Args...) |
|||
} else { |
|||
conditionDB = this.context.DB.Model(&Matter{}).Where(wp.Query, wp.Args...) |
|||
} |
|||
|
|||
count := 0 |
|||
db := conditionDB.Count(&count) |
|||
this.PanicError(db.Error) |
|||
|
|||
var matters []*Matter |
|||
db = conditionDB.Order(this.GetSortString(sortArray)).Offset(page * pageSize).Limit(pageSize).Find(&matters) |
|||
this.PanicError(db.Error) |
|||
pager := NewPager(page, pageSize, count, matters) |
|||
|
|||
return pager |
|||
} |
|||
|
|||
//创建
|
|||
func (this *MatterDao) Create(matter *Matter) *Matter { |
|||
|
|||
timeUUID, _ := uuid.NewV4() |
|||
matter.Uuid = string(timeUUID.String()) |
|||
matter.CreateTime = time.Now() |
|||
matter.ModifyTime = time.Now() |
|||
db := this.context.DB.Create(matter) |
|||
this.PanicError(db.Error) |
|||
|
|||
return matter |
|||
} |
|||
|
|||
//修改一个文件
|
|||
func (this *MatterDao) Save(matter *Matter) *Matter { |
|||
|
|||
matter.ModifyTime = time.Now() |
|||
db := this.context.DB.Save(matter) |
|||
this.PanicError(db.Error) |
|||
|
|||
return matter |
|||
} |
|||
|
|||
//删除一个文件,数据库中删除,物理磁盘上删除。
|
|||
func (this *MatterDao) Delete(matter *Matter) { |
|||
|
|||
//目录的话递归删除。
|
|||
if matter.Dir { |
|||
matters := this.List(matter.Uuid, matter.UserUuid, nil) |
|||
|
|||
for _, f := range matters { |
|||
this.Delete(f) |
|||
|
|||
} |
|||
|
|||
//删除文件夹本身
|
|||
db := this.context.DB.Delete(&matter) |
|||
this.PanicError(db.Error) |
|||
|
|||
} else { |
|||
db := this.context.DB.Delete(&matter) |
|||
this.PanicError(db.Error) |
|||
|
|||
//删除文件
|
|||
err := os.Remove(GetFilePath() + matter.Path) |
|||
|
|||
LogError(fmt.Sprintf("删除磁盘上的文件出错,不做任何处理")) |
|||
|
|||
this.PanicError(err) |
|||
|
|||
} |
|||
} |
|||
@ -0,0 +1,19 @@ |
|||
package rest |
|||
|
|||
type Matter struct { |
|||
Base |
|||
Puuid string `json:"puuid"` |
|||
UserUuid string `json:"userUuid"` |
|||
Dir bool `json:"dir"` |
|||
Name string `json:"name"` |
|||
Md5 string `json:"md5"` |
|||
Size int64 `json:"size"` |
|||
Privacy bool `json:"privacy"` |
|||
Path string `json:"path"` |
|||
Parent *Matter `gorm:"-" json:"parent"` |
|||
} |
|||
|
|||
// set File's table name to be `profiles`
|
|||
func (Matter) TableName() string { |
|||
return TABLE_PREFIX + "matter" |
|||
} |
|||
@ -0,0 +1,117 @@ |
|||
package rest |
|||
|
|||
import ( |
|||
"io" |
|||
"mime/multipart" |
|||
"os" |
|||
"regexp" |
|||
"strings" |
|||
|
|||
) |
|||
|
|||
//@Service
|
|||
type MatterService struct { |
|||
Bean |
|||
matterDao *MatterDao |
|||
} |
|||
|
|||
//初始化方法
|
|||
func (this *MatterService) Init(context *Context) { |
|||
|
|||
//手动装填本实例的Bean. 这里必须要用中间变量方可。
|
|||
|
|||
b := context.GetBean(this.matterDao) |
|||
if b, ok := b.(*MatterDao); ok { |
|||
this.matterDao = b |
|||
} |
|||
|
|||
} |
|||
|
|||
//根据一个文件夹路径,找到最后一个文件夹的uuid,如果中途出错,返回err.
|
|||
func (this *MatterService) GetDirUuid(userUuid string, dir string) string { |
|||
|
|||
if dir == "" { |
|||
panic(`文件夹不能为空`) |
|||
} else if dir[0:1] != "/" { |
|||
panic(`文件夹必须以/开头`) |
|||
} else if strings.Index(dir, "//") != -1 { |
|||
panic(`文件夹不能出现连续的//`) |
|||
} else if m, _ := regexp.MatchString(`[<>|*?\\]`, dir); m { |
|||
panic(`文件夹中不能包含以下特殊符号:< > | * ? \`) |
|||
} |
|||
|
|||
if dir == "/" { |
|||
return "root" |
|||
} |
|||
|
|||
if dir[len(dir)-1] == '/' { |
|||
dir = dir[:len(dir)-1] |
|||
} |
|||
|
|||
//递归找寻文件的上级目录uuid.
|
|||
folders := strings.Split(dir, "/") |
|||
|
|||
puuid := "root" |
|||
for k, name := range folders { |
|||
if k == 0 { |
|||
continue |
|||
} |
|||
|
|||
matter := this.matterDao.FindByUserUuidAndPuuidAndNameAndDirTrue(userUuid, puuid, name) |
|||
if matter == nil { |
|||
//创建一个文件夹。
|
|||
matter = &Matter{ |
|||
Puuid: puuid, |
|||
UserUuid: userUuid, |
|||
Dir: true, |
|||
Name: name, |
|||
} |
|||
matter = this.matterDao.Create(matter) |
|||
} |
|||
|
|||
puuid = matter.Uuid |
|||
} |
|||
|
|||
return puuid |
|||
} |
|||
|
|||
//开始上传文件
|
|||
//上传文件
|
|||
func (this *MatterService) Upload(file multipart.File, user *User, puuid string, filename string, privacy bool) *Matter { |
|||
|
|||
//获取文件应该存放在的物理路径的绝对路径和相对路径。
|
|||
absolutePath, relativePath := GetUserFilePath(user.Username) |
|||
absolutePath = absolutePath + "/" + filename |
|||
relativePath = relativePath + "/" + filename |
|||
|
|||
distFile, err := os.OpenFile(absolutePath, os.O_WRONLY|os.O_CREATE, 0777) |
|||
this.PanicError(err) |
|||
|
|||
defer distFile.Close() |
|||
|
|||
written, err := io.Copy(distFile, file) |
|||
this.PanicError(err) |
|||
|
|||
//查找文件夹下面是否有同名文件。
|
|||
matters := this.matterDao.ListByUserUuidAndPuuidAndDirAndName(user.Uuid, puuid, false, filename) |
|||
//如果有同名的文件,那么我们直接覆盖同名文件。
|
|||
for _, dbFile := range matters { |
|||
this.matterDao.Delete(dbFile) |
|||
} |
|||
|
|||
//将文件信息存入数据库中。
|
|||
matter := &Matter{ |
|||
Puuid: puuid, |
|||
UserUuid: user.Uuid, |
|||
Dir: false, |
|||
Name: filename, |
|||
Md5: "", |
|||
Size: written, |
|||
Privacy: privacy, |
|||
Path: relativePath, |
|||
} |
|||
|
|||
matter = this.matterDao.Create(matter) |
|||
|
|||
return matter |
|||
} |
|||
@ -0,0 +1,138 @@ |
|||
package rest |
|||
|
|||
import ( |
|||
"fmt" |
|||
"github.com/json-iterator/go" |
|||
"io" |
|||
"net/http" |
|||
"os" |
|||
"strings" |
|||
|
|||
) |
|||
|
|||
//用于处理所有前来的请求
|
|||
type Router struct { |
|||
context *Context |
|||
routeMap map[string]func(writer http.ResponseWriter, request *http.Request) |
|||
} |
|||
|
|||
//构造方法
|
|||
func NewRouter(context *Context) *Router { |
|||
router := &Router{ |
|||
context: context, |
|||
routeMap: make(map[string]func(writer http.ResponseWriter, request *http.Request)), |
|||
} |
|||
|
|||
for _, controller := range context.ControllerMap { |
|||
routes := controller.RegisterRoutes() |
|||
for k, v := range routes { |
|||
router.routeMap[k] = v |
|||
} |
|||
} |
|||
return router |
|||
|
|||
} |
|||
|
|||
//全局的异常捕获
|
|||
func (this *Router) GlobalPanicHandler(writer http.ResponseWriter, request *http.Request) { |
|||
if err := recover(); err != nil { |
|||
|
|||
LogError(fmt.Sprintf("全局异常: %v", err)) |
|||
|
|||
var webResult *WebResult = nil |
|||
if value, ok := err.(string); ok { |
|||
webResult = &WebResult{Code: RESULT_CODE_UTIL_EXCEPTION, Msg: value} |
|||
} else if value, ok := err.(int); ok { |
|||
webResult = ConstWebResult(value) |
|||
} else if value, ok := err.(*WebResult); ok { |
|||
webResult = value |
|||
} else if value, ok := err.(WebResult); ok { |
|||
webResult = &value |
|||
} else if value, ok := err.(error); ok { |
|||
webResult = &WebResult{Code: RESULT_CODE_UTIL_EXCEPTION, Msg: value.Error()} |
|||
} else { |
|||
webResult = &WebResult{Code: RESULT_CODE_UTIL_EXCEPTION, Msg: "服务器未知错误"} |
|||
} |
|||
|
|||
//输出的是json格式 返回的内容申明是json,utf-8
|
|||
writer.Header().Set("Content-Type", "application/json;charset=UTF-8") |
|||
|
|||
//用json的方式输出返回值。
|
|||
var json = jsoniter.ConfigCompatibleWithStandardLibrary |
|||
b, _ := json.Marshal(webResult) |
|||
|
|||
if webResult.Code == RESULT_CODE_OK { |
|||
writer.WriteHeader(http.StatusOK) |
|||
} else { |
|||
writer.WriteHeader(http.StatusBadRequest) |
|||
} |
|||
fmt.Fprintf(writer, string(b)) |
|||
} |
|||
} |
|||
|
|||
//让Router具有处理请求的功能。
|
|||
func (this *Router) ServeHTTP(writer http.ResponseWriter, request *http.Request) { |
|||
|
|||
//每个请求的入口在这里
|
|||
//全局异常处理。
|
|||
defer this.GlobalPanicHandler(writer, request) |
|||
|
|||
path := request.URL.Path |
|||
if strings.HasPrefix(path, "/api") { |
|||
|
|||
if handler, ok := this.routeMap[path]; ok { |
|||
|
|||
handler(writer, request) |
|||
|
|||
} else { |
|||
//直接将请求扔给每个controller,看看他们能不能处理,如果都不能处理,那就算了。
|
|||
canHandle := false |
|||
for _, controller := range this.context.ControllerMap { |
|||
if handler, exist := controller.HandleRoutes(writer, request); exist { |
|||
canHandle = true |
|||
|
|||
handler(writer, request) |
|||
break |
|||
} |
|||
} |
|||
|
|||
if !canHandle { |
|||
panic(fmt.Sprintf("没有找到能够处理%s的方法\n", path)) |
|||
} |
|||
|
|||
} |
|||
|
|||
} else { |
|||
//当作静态资源处理。默认从当前文件下面的static文件夹中取东西。
|
|||
dir := GetHtmlPath() |
|||
|
|||
requestURI := request.RequestURI |
|||
if requestURI == "" || request.RequestURI == "/" { |
|||
requestURI = "index.html" |
|||
} |
|||
|
|||
filePath := dir + requestURI |
|||
exists, _ := PathExists(filePath) |
|||
if !exists { |
|||
filePath = dir + "/index.html" |
|||
exists, _ = PathExists(filePath) |
|||
if !exists { |
|||
panic("404 not found") |
|||
} |
|||
} |
|||
|
|||
writer.Header().Set("Content-Type", GetMimeType(GetExtension(filePath))) |
|||
|
|||
diskFile, err := os.Open(filePath) |
|||
if err != nil { |
|||
panic("cannot get file.") |
|||
} |
|||
defer diskFile.Close() |
|||
_, err = io.Copy(writer, diskFile) |
|||
if err != nil { |
|||
panic("cannot get file.") |
|||
} |
|||
|
|||
} |
|||
|
|||
} |
|||
@ -0,0 +1,75 @@ |
|||
package rest |
|||
|
|||
import ( |
|||
_ "github.com/jinzhu/gorm/dialects/mysql" |
|||
"github.com/nu7hatch/gouuid" |
|||
"time" |
|||
) |
|||
|
|||
type SessionDao struct { |
|||
BaseDao |
|||
} |
|||
|
|||
//构造函数
|
|||
func NewSessionDao(context *Context) *SessionDao { |
|||
|
|||
var sessionDao = &SessionDao{} |
|||
sessionDao.Init(context) |
|||
return sessionDao |
|||
} |
|||
|
|||
//按照Id查询session.
|
|||
func (this *SessionDao) FindByUuid(uuid string) *Session { |
|||
|
|||
// Read
|
|||
var session = &Session{} |
|||
db := this.context.DB.Where(&Session{Base: Base{Uuid: uuid}}).First(session) |
|||
if db.Error != nil { |
|||
return nil |
|||
} |
|||
return session |
|||
} |
|||
|
|||
//按照Id查询session.
|
|||
func (this *SessionDao) CheckByUuid(uuid string) *Session { |
|||
|
|||
// Read
|
|||
var session = &Session{} |
|||
db := this.context.DB.Where(&Session{Base: Base{Uuid: uuid}}).First(session) |
|||
this.PanicError(db.Error) |
|||
return session |
|||
} |
|||
|
|||
//按照authentication查询用户。
|
|||
func (this *SessionDao) FindByAuthentication(authentication string) *Session { |
|||
|
|||
var session = &Session{} |
|||
db := this.context.DB.Where(&Session{Authentication: authentication}).First(session) |
|||
if db.Error != nil { |
|||
return nil |
|||
} |
|||
return session |
|||
|
|||
} |
|||
|
|||
//创建一个session并且持久化到数据库中。
|
|||
func (this *SessionDao) Create(session *Session) *Session { |
|||
|
|||
timeUUID, _ := uuid.NewV4() |
|||
session.Uuid = string(timeUUID.String()) |
|||
db := this.context.DB.Create(session) |
|||
this.PanicError(db.Error) |
|||
|
|||
return session |
|||
} |
|||
|
|||
func (this *SessionDao) Delete(uuid string) { |
|||
|
|||
session := this.CheckByUuid(uuid) |
|||
|
|||
session.ExpireTime = time.Now() |
|||
db := this.context.DB.Delete(session) |
|||
|
|||
this.PanicError(db.Error) |
|||
|
|||
} |
|||
@ -0,0 +1,18 @@ |
|||
package rest |
|||
|
|||
import ( |
|||
"time" |
|||
) |
|||
|
|||
type Session struct { |
|||
Base |
|||
Authentication string `json:"authentication"` |
|||
UserUuid string `json:"userUuid"` |
|||
Ip string `json:"ip"` |
|||
ExpireTime time.Time `json:"expireTime"` |
|||
} |
|||
|
|||
// set User's table name to be `profiles`
|
|||
func (Session) TableName() string { |
|||
return TABLE_PREFIX + "session" |
|||
} |
|||
@ -0,0 +1,48 @@ |
|||
package rest |
|||
|
|||
type OrderPair struct { |
|||
key string |
|||
value string |
|||
} |
|||
|
|||
type WherePair struct { |
|||
Query string |
|||
Args []interface{} |
|||
} |
|||
|
|||
func (this *WherePair) And(where *WherePair) *WherePair { |
|||
if this.Query == "" { |
|||
return where |
|||
} else { |
|||
return &WherePair{Query: this.Query + " AND " + where.Query, Args: append(this.Args, where.Args...)} |
|||
} |
|||
|
|||
} |
|||
|
|||
func (this *WherePair) Or(where *WherePair) *WherePair { |
|||
if this.Query == "" { |
|||
return where |
|||
} else { |
|||
return &WherePair{Query: this.Query + " OR " + where.Query, Args: append(this.Args, where.Args...)} |
|||
} |
|||
|
|||
} |
|||
|
|||
//根据一个sortMap,获取到order字符串
|
|||
func (this *BaseDao) GetSortString(sortArray []OrderPair) string { |
|||
|
|||
if sortArray == nil || len(sortArray) == 0 { |
|||
return "" |
|||
} |
|||
str := "" |
|||
for _, pair := range sortArray { |
|||
if pair.value == "DESC" || pair.value == "ASC" { |
|||
if str != "" { |
|||
str = str + "," |
|||
} |
|||
str = str + " " + pair.key + " " + pair.value |
|||
} |
|||
} |
|||
|
|||
return str |
|||
} |
|||
@ -0,0 +1,50 @@ |
|||
package rest |
|||
|
|||
import ( |
|||
_ "github.com/jinzhu/gorm/dialects/mysql" |
|||
"github.com/nu7hatch/gouuid" |
|||
"time" |
|||
) |
|||
|
|||
type UploadTokenDao struct { |
|||
BaseDao |
|||
} |
|||
|
|||
//按照Id查询
|
|||
func (this *UploadTokenDao) FindByUuid(uuid string) *UploadToken { |
|||
|
|||
// Read
|
|||
var uploadToken = &UploadToken{} |
|||
db := this.context.DB.Where(&UploadToken{Base: Base{Uuid: uuid}}).First(uploadToken) |
|||
if db.Error != nil { |
|||
return nil |
|||
} |
|||
|
|||
return uploadToken |
|||
|
|||
} |
|||
|
|||
//创建一个session并且持久化到数据库中。
|
|||
func (this *UploadTokenDao) Create(uploadToken *UploadToken) *UploadToken { |
|||
|
|||
timeUUID, _ := uuid.NewV4() |
|||
uploadToken.Uuid = string(timeUUID.String()) |
|||
|
|||
uploadToken.CreateTime = time.Now() |
|||
uploadToken.ModifyTime = time.Now() |
|||
|
|||
db := this.context.DB.Create(uploadToken) |
|||
this.PanicError(db.Error) |
|||
|
|||
return uploadToken |
|||
} |
|||
|
|||
//修改一个uploadToken
|
|||
func (this *UploadTokenDao) Save(uploadToken *UploadToken) *UploadToken { |
|||
|
|||
uploadToken.ModifyTime = time.Now() |
|||
db := this.context.DB.Save(uploadToken) |
|||
this.PanicError(db.Error) |
|||
|
|||
return uploadToken |
|||
} |
|||
@ -0,0 +1,21 @@ |
|||
package rest |
|||
|
|||
import ( |
|||
"time" |
|||
) |
|||
|
|||
type UploadToken struct { |
|||
Base |
|||
UserUuid string `json:"userUuid"` |
|||
FolderUuid string `json:"folderUuid"` |
|||
MatterUuid string `json:"matterUuid"` |
|||
ExpireTime time.Time `json:"expireTime"` |
|||
Filename string `json:"filename"` |
|||
Privacy bool `json:"privacy"` |
|||
Size int64 `json:"size"` |
|||
Ip string `json:"ip"` |
|||
} |
|||
|
|||
func (UploadToken) TableName() string { |
|||
return TABLE_PREFIX + "upload_token" |
|||
} |
|||
@ -0,0 +1,322 @@ |
|||
package rest |
|||
|
|||
import ( |
|||
"net/http" |
|||
"regexp" |
|||
"strconv" |
|||
|
|||
"time" |
|||
) |
|||
|
|||
type UserController struct { |
|||
BaseController |
|||
} |
|||
|
|||
//初始化方法
|
|||
func (this *UserController) Init(context *Context) { |
|||
this.BaseController.Init(context) |
|||
} |
|||
|
|||
//注册自己的路由。
|
|||
func (this *UserController) RegisterRoutes() map[string]func(writer http.ResponseWriter, request *http.Request) { |
|||
|
|||
routeMap := make(map[string]func(writer http.ResponseWriter, request *http.Request)) |
|||
|
|||
//每个Controller需要主动注册自己的路由。
|
|||
routeMap["/api/user/create"] = this.Wrap(this.Create, USER_ROLE_ADMINISTRATOR) |
|||
routeMap["/api/user/edit"] = this.Wrap(this.Edit, USER_ROLE_ADMINISTRATOR) |
|||
routeMap["/api/user/change/password"] = this.Wrap(this.ChangePassword, USER_ROLE_USER) |
|||
routeMap["/api/user/reset/password"] = this.Wrap(this.ResetPassword, USER_ROLE_ADMINISTRATOR) |
|||
routeMap["/api/user/login"] = this.Wrap(this.Login, USER_ROLE_GUEST) |
|||
routeMap["/api/user/logout"] = this.Wrap(this.Logout, USER_ROLE_USER) |
|||
routeMap["/api/user/detail"] = this.Wrap(this.Detail, USER_ROLE_USER) |
|||
routeMap["/api/user/page"] = this.Wrap(this.Page, USER_ROLE_ADMINISTRATOR) |
|||
routeMap["/api/user/disable"] = this.Wrap(this.Disable, USER_ROLE_ADMINISTRATOR) |
|||
routeMap["/api/user/enable"] = this.Wrap(this.Enable, USER_ROLE_ADMINISTRATOR) |
|||
|
|||
return routeMap |
|||
} |
|||
|
|||
//使用用户名和密码进行登录。
|
|||
//参数:
|
|||
// @email:邮箱
|
|||
// @password:密码
|
|||
func (this *UserController) Login(writer http.ResponseWriter, request *http.Request) *WebResult { |
|||
|
|||
email := request.FormValue("email") |
|||
password := request.FormValue("password") |
|||
|
|||
if "" == email || "" == password { |
|||
|
|||
return this.Error("请输入邮箱和密码") |
|||
} |
|||
|
|||
user := this.userDao.FindByEmail(email) |
|||
if user == nil { |
|||
|
|||
return this.Error("邮箱或密码错误") |
|||
} else { |
|||
if !MatchBcrypt(password, user.Password) { |
|||
|
|||
return this.Error("邮箱或密码错误") |
|||
} |
|||
} |
|||
|
|||
//登录成功,设置Cookie。有效期7天。
|
|||
expiration := time.Now() |
|||
expiration = expiration.AddDate(0, 0, 7) |
|||
|
|||
//持久化用户的session.
|
|||
|
|||
session := &Session{ |
|||
UserUuid: user.Uuid, |
|||
Ip: GetIpAddress(request), |
|||
ExpireTime: expiration, |
|||
} |
|||
session.ModifyTime = time.Now() |
|||
session.CreateTime = time.Now() |
|||
session = this.sessionDao.Create(session) |
|||
|
|||
//设置用户的cookie.
|
|||
cookie := http.Cookie{ |
|||
Name: COOKIE_AUTH_KEY, |
|||
Path: "/", |
|||
Value: session.Uuid, |
|||
Expires: expiration} |
|||
http.SetCookie(writer, &cookie) |
|||
|
|||
return this.Success(user) |
|||
} |
|||
|
|||
//创建一个用户
|
|||
func (this *UserController) Create(writer http.ResponseWriter, request *http.Request) *WebResult { |
|||
|
|||
username := request.FormValue("username") |
|||
if m, _ := regexp.MatchString(`^[0-9a-zA-Z_]+$`, username); !m { |
|||
return this.Error(`用户名必填,且只能包含字母,数字和'_''`) |
|||
} |
|||
password := request.FormValue("password") |
|||
if len(password) < 6 { |
|||
return this.Error(`密码长度至少为6位`) |
|||
} |
|||
|
|||
email := request.FormValue("email") |
|||
if email == "" { |
|||
return this.Error("邮箱必填!") |
|||
} |
|||
|
|||
phone := request.FormValue("phone") |
|||
gender := request.FormValue("gender") |
|||
role := request.FormValue("role") |
|||
city := request.FormValue("city") |
|||
|
|||
//判断重名。
|
|||
if this.userDao.CountByUsername(username) > 0 { |
|||
return this.Error(username + "已经被其他用户占用。") |
|||
} |
|||
//判断邮箱重名
|
|||
if this.userDao.CountByEmail(email) > 0 { |
|||
return this.Error(email + "已经被其他用户占用。") |
|||
} |
|||
|
|||
user := &User{ |
|||
Role: GetRole(role), |
|||
Username: username, |
|||
Password: GetBcrypt(password), |
|||
Email: email, |
|||
Phone: phone, |
|||
Gender: gender, |
|||
City: city, |
|||
Status: USER_STATUS_OK, |
|||
} |
|||
|
|||
user = this.userDao.Create(user) |
|||
|
|||
return this.Success(user) |
|||
} |
|||
|
|||
//编辑一个用户的资料。
|
|||
func (this *UserController) Edit(writer http.ResponseWriter, request *http.Request) *WebResult { |
|||
|
|||
uuid := request.FormValue("uuid") |
|||
phone := request.FormValue("phone") |
|||
gender := request.FormValue("gender") |
|||
city := request.FormValue("city") |
|||
|
|||
currentUser := this.checkUser(writer, request) |
|||
if currentUser.Role != USER_ROLE_ADMINISTRATOR { |
|||
if currentUser.Uuid != uuid { |
|||
return this.Error(RESULT_CODE_UNAUTHORIZED) |
|||
} |
|||
} |
|||
|
|||
user := this.userDao.CheckByUuid(uuid) |
|||
|
|||
user.Phone = phone |
|||
user.Gender = GetGender(gender) |
|||
user.City = city |
|||
|
|||
user = this.userDao.Save(user) |
|||
|
|||
return this.Success(user) |
|||
} |
|||
|
|||
//获取用户详情
|
|||
func (this *UserController) Detail(writer http.ResponseWriter, request *http.Request) *WebResult { |
|||
|
|||
uuid := request.FormValue("uuid") |
|||
|
|||
user := this.userDao.CheckByUuid(uuid) |
|||
|
|||
return this.Success(user) |
|||
|
|||
} |
|||
|
|||
//退出登录
|
|||
func (this *UserController) Logout(writer http.ResponseWriter, request *http.Request) *WebResult { |
|||
|
|||
session, _ := this.checkLogin(writer, request) |
|||
|
|||
//删除session
|
|||
this.sessionDao.Delete(session.Uuid) |
|||
|
|||
//清空客户端的cookie.
|
|||
expiration := time.Now() |
|||
expiration = expiration.AddDate(-1, 0, 0) |
|||
cookie := http.Cookie{ |
|||
Name: COOKIE_AUTH_KEY, |
|||
Path: "/", |
|||
Value: session.Uuid, |
|||
Expires: expiration} |
|||
http.SetCookie(writer, &cookie) |
|||
|
|||
return this.Success("退出成功!") |
|||
} |
|||
|
|||
//获取用户列表 管理员的权限。
|
|||
func (this *UserController) Page(writer http.ResponseWriter, request *http.Request) *WebResult { |
|||
|
|||
//如果是根目录,那么就传入root.
|
|||
pageStr := request.FormValue("page") |
|||
pageSizeStr := request.FormValue("pageSize") |
|||
username := request.FormValue("username") |
|||
email := request.FormValue("email") |
|||
phone := request.FormValue("phone") |
|||
orderLastTime := request.FormValue("orderLastTime") |
|||
orderCreateTime := request.FormValue("orderCreateTime") |
|||
|
|||
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: "last_time", |
|||
value: orderLastTime, |
|||
}, |
|||
{ |
|||
key: "create_time", |
|||
value: orderCreateTime, |
|||
}, |
|||
} |
|||
|
|||
pager := this.userDao.Page(page, pageSize, username, email, phone, sortArray) |
|||
|
|||
return this.Success(pager) |
|||
} |
|||
|
|||
//禁用用户
|
|||
func (this *UserController) Disable(writer http.ResponseWriter, request *http.Request) *WebResult { |
|||
|
|||
uuid := request.FormValue("uuid") |
|||
|
|||
user := this.userDao.CheckByUuid(uuid) |
|||
|
|||
if user.Status == USER_STATUS_DISABLED { |
|||
return this.Error("用户已经被禁用,操作无效。") |
|||
} |
|||
|
|||
user.Status = USER_STATUS_DISABLED |
|||
|
|||
user = this.userDao.Save(user) |
|||
|
|||
return this.Success(user) |
|||
|
|||
} |
|||
|
|||
//启用用户
|
|||
func (this *UserController) Enable(writer http.ResponseWriter, request *http.Request) *WebResult { |
|||
|
|||
uuid := request.FormValue("uuid") |
|||
|
|||
user := this.userDao.CheckByUuid(uuid) |
|||
|
|||
if user.Status == USER_STATUS_OK { |
|||
return this.Error("用户已经是正常状态,操作无效。") |
|||
} |
|||
|
|||
user.Status = USER_STATUS_OK |
|||
|
|||
user = this.userDao.Save(user) |
|||
|
|||
return this.Success(user) |
|||
|
|||
} |
|||
|
|||
//用户修改密码
|
|||
func (this *UserController) ChangePassword(writer http.ResponseWriter, request *http.Request) *WebResult { |
|||
|
|||
oldPassword := request.FormValue("oldPassword") |
|||
newPassword := request.FormValue("newPassword") |
|||
if oldPassword == "" || newPassword == "" { |
|||
return this.Error("旧密码和新密码都不能为空") |
|||
} |
|||
|
|||
user := this.checkUser(writer, request) |
|||
|
|||
if !MatchBcrypt(oldPassword, user.Password) { |
|||
return this.Error("旧密码不正确!") |
|||
} |
|||
|
|||
user.Password = GetBcrypt(newPassword) |
|||
|
|||
user = this.userDao.Save(user) |
|||
|
|||
return this.Success(user) |
|||
} |
|||
|
|||
//管理员重置用户密码
|
|||
func (this *UserController) ResetPassword(writer http.ResponseWriter, request *http.Request) *WebResult { |
|||
|
|||
userUuid := request.FormValue("userUuid") |
|||
password := request.FormValue("password") |
|||
if userUuid == "" { |
|||
return this.Error("用户不能为空") |
|||
} |
|||
if password == "" { |
|||
return this.Error("密码不能为空") |
|||
} |
|||
|
|||
currentUser := this.checkUser(writer, request) |
|||
|
|||
if currentUser.Role != USER_ROLE_ADMINISTRATOR { |
|||
return this.Error(RESULT_CODE_UNAUTHORIZED) |
|||
} |
|||
|
|||
user := this.userDao.CheckByUuid(userUuid) |
|||
|
|||
user.Password = GetBcrypt(password) |
|||
|
|||
user = this.userDao.Save(user) |
|||
|
|||
return this.Success(currentUser) |
|||
} |
|||
@ -0,0 +1,132 @@ |
|||
package rest |
|||
|
|||
import ( |
|||
_ "github.com/jinzhu/gorm/dialects/mysql" |
|||
"github.com/nu7hatch/gouuid" |
|||
"time" |
|||
) |
|||
|
|||
type UserDao struct { |
|||
BaseDao |
|||
} |
|||
|
|||
//创建用户
|
|||
func (this *UserDao) Create(user *User) *User { |
|||
|
|||
if user == nil { |
|||
panic("参数不能为nil") |
|||
} |
|||
|
|||
timeUUID, _ := uuid.NewV4() |
|||
user.Uuid = string(timeUUID.String()) |
|||
user.CreateTime = time.Now() |
|||
user.ModifyTime = time.Now() |
|||
user.LastTime = time.Now() |
|||
user.Sort = time.Now().UnixNano() / 1e6 |
|||
|
|||
db := this.context.DB.Create(user) |
|||
this.PanicError(db.Error) |
|||
|
|||
return user |
|||
} |
|||
|
|||
//按照Id查询用户,找不到返回nil
|
|||
func (this *UserDao) FindByUuid(uuid string) *User { |
|||
|
|||
// Read
|
|||
var user *User = &User{} |
|||
db := this.context.DB.Where(&User{Base: Base{Uuid: uuid}}).First(user) |
|||
if db.Error != nil { |
|||
return nil |
|||
} |
|||
return user |
|||
} |
|||
|
|||
//按照Id查询用户,找不到抛panic
|
|||
func (this *UserDao) CheckByUuid(uuid string) *User { |
|||
|
|||
// Read
|
|||
var user *User = &User{} |
|||
db := this.context.DB.Where(&User{Base: Base{Uuid: uuid}}).First(user) |
|||
this.PanicError(db.Error) |
|||
return user |
|||
} |
|||
|
|||
//按照邮箱查询用户。
|
|||
func (this *UserDao) FindByEmail(email string) *User { |
|||
|
|||
var user *User = &User{} |
|||
db := this.context.DB.Where(&User{Email: email}).First(user) |
|||
if db.Error != nil { |
|||
return nil |
|||
} |
|||
return user |
|||
} |
|||
|
|||
//显示用户列表。
|
|||
func (this *UserDao) Page(page int, pageSize int, username string, email string, phone string, sortArray []OrderPair) *Pager { |
|||
|
|||
var wp = &WherePair{} |
|||
|
|||
if username != "" { |
|||
wp = wp.And(&WherePair{Query: "username LIKE ?", Args: []interface{}{"%" + username + "%"}}) |
|||
} |
|||
|
|||
if email != "" { |
|||
wp = wp.And(&WherePair{Query: "email LIKE ?", Args: []interface{}{"%" + email + "%"}}) |
|||
} |
|||
|
|||
if phone != "" { |
|||
wp = wp.And(&WherePair{Query: "phone = ?", Args: []interface{}{phone}}) |
|||
} |
|||
|
|||
count := 0 |
|||
db := this.context.DB.Model(&User{}).Where(wp.Query, wp.Args...).Count(&count) |
|||
this.PanicError(db.Error) |
|||
|
|||
var users []*User |
|||
orderStr := this.GetSortString(sortArray) |
|||
if orderStr == "" { |
|||
db = this.context.DB.Where(wp.Query, wp.Args...).Offset(page * pageSize).Limit(pageSize).Find(&users) |
|||
} else { |
|||
db = this.context.DB.Where(wp.Query, wp.Args...).Order(orderStr).Offset(page * pageSize).Limit(pageSize).Find(&users) |
|||
} |
|||
|
|||
this.PanicError(db.Error) |
|||
|
|||
pager := NewPager(page, pageSize, count, users) |
|||
|
|||
return pager |
|||
} |
|||
|
|||
//查询某个用户名是否已经有用户了
|
|||
func (this *UserDao) CountByUsername(username string) int { |
|||
var count int |
|||
db := this.context.DB. |
|||
Model(&User{}). |
|||
Where("username = ?", username). |
|||
Count(&count) |
|||
this.PanicError(db.Error) |
|||
return count |
|||
} |
|||
|
|||
//查询某个邮箱是否已经有用户了
|
|||
func (this *UserDao) CountByEmail(email string) int { |
|||
var count int |
|||
db := this.context.DB. |
|||
Model(&User{}). |
|||
Where("email = ?", email). |
|||
Count(&count) |
|||
this.PanicError(db.Error) |
|||
return count |
|||
} |
|||
|
|||
//保存用户
|
|||
func (this *UserDao) Save(user *User) *User { |
|||
|
|||
user.ModifyTime = time.Now() |
|||
db := this.context.DB. |
|||
Save(user) |
|||
this.PanicError(db.Error) |
|||
return user |
|||
} |
|||
@ -0,0 +1,63 @@ |
|||
package rest |
|||
|
|||
import ( |
|||
"time" |
|||
) |
|||
|
|||
const ( |
|||
//游客身份
|
|||
USER_ROLE_GUEST = "GUEST" |
|||
//普通注册用户
|
|||
USER_ROLE_USER = "USER" |
|||
//管理员
|
|||
USER_ROLE_ADMINISTRATOR = "ADMINISTRATOR" |
|||
) |
|||
|
|||
const ( |
|||
USER_GENDER_MALE = "MALE" |
|||
USER_GENDER_FEMALE = "FEMALE" |
|||
USER_GENDER_UNKNOWN = "UNKNOWN" |
|||
) |
|||
|
|||
const ( |
|||
USER_STATUS_OK = "OK" |
|||
USER_STATUS_DISABLED = "DISABLED" |
|||
) |
|||
|
|||
type User struct { |
|||
Base |
|||
Role string `json:"role"` |
|||
Username string `json:"username"` |
|||
Password string `json:"-"` |
|||
Email string `json:"email"` |
|||
Phone string `json:"phone"` |
|||
Gender string `json:"gender"` |
|||
City string `json:"city"` |
|||
AvatarUrl string `json:"avatarUrl"` |
|||
LastIp string `json:"lastIp"` |
|||
LastTime time.Time `json:"lastTime"` |
|||
Status string `json:"status"` |
|||
} |
|||
|
|||
// set User's table name to be `profiles`
|
|||
func (User) TableName() string { |
|||
return TABLE_PREFIX + "user" |
|||
} |
|||
|
|||
//通过一个字符串获取性别
|
|||
func GetGender(genderString string) string { |
|||
if genderString == USER_GENDER_MALE || genderString == USER_GENDER_FEMALE || genderString == USER_GENDER_UNKNOWN { |
|||
return genderString |
|||
} else { |
|||
return USER_GENDER_UNKNOWN |
|||
} |
|||
} |
|||
|
|||
//通过一个字符串获取角色
|
|||
func GetRole(roleString string) string { |
|||
if roleString == USER_ROLE_USER || roleString == USER_ROLE_ADMINISTRATOR { |
|||
return roleString |
|||
} else { |
|||
return USER_ROLE_USER |
|||
} |
|||
} |
|||
@ -0,0 +1,30 @@ |
|||
package rest |
|||
|
|||
import ( |
|||
"golang.org/x/crypto/bcrypt" |
|||
"fmt" |
|||
"crypto/md5" |
|||
) |
|||
|
|||
//给密码字符串加密
|
|||
func GetMd5(raw string) string { |
|||
return fmt.Sprintf("%x", md5.Sum([]byte(raw))) |
|||
} |
|||
|
|||
func GetBcrypt(raw string) string { |
|||
|
|||
password := []byte(raw) |
|||
hashedPassword, err := bcrypt.GenerateFromPassword(password, bcrypt.DefaultCost) |
|||
if err != nil { |
|||
panic(err) |
|||
} |
|||
return string(hashedPassword) |
|||
} |
|||
|
|||
func MatchBcrypt(raw string, bcryptStr string) bool { |
|||
|
|||
err := bcrypt.CompareHashAndPassword([]byte(bcryptStr), []byte(raw)) |
|||
|
|||
return err == nil |
|||
|
|||
} |
|||
@ -0,0 +1,50 @@ |
|||
package rest |
|||
|
|||
import ( |
|||
"fmt" |
|||
"log" |
|||
"os" |
|||
"time" |
|||
) |
|||
|
|||
func Log(prefix string, content string) { |
|||
|
|||
//日志输出到文件中
|
|||
filePath := GetLogPath() + "/tank-" + time.Now().Local().Format("2006-01-02") + ".log" |
|||
f, err := os.OpenFile(filePath, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666) |
|||
if err != nil { |
|||
fmt.Errorf("error opening file: %v", err) |
|||
} |
|||
defer f.Close() |
|||
|
|||
log.SetOutput(f) |
|||
log.SetPrefix(prefix) |
|||
log.Println(content) |
|||
|
|||
//如果需要输出到控制台。
|
|||
if CONFIG.LogToConsole { |
|||
fmt.Println(content) |
|||
} |
|||
|
|||
} |
|||
|
|||
func LogDebug(content string) { |
|||
go Log("[Debug]", content) |
|||
} |
|||
|
|||
func LogInfo(content string) { |
|||
go Log("[Info]", content) |
|||
} |
|||
|
|||
func LogWarning(content string) { |
|||
go Log("[Warning]", content) |
|||
} |
|||
|
|||
func LogError(content string) { |
|||
go Log("[Error]", content) |
|||
} |
|||
|
|||
func LogPanic(content interface{}) { |
|||
Log("[Panic]", fmt.Sprintf("%v", content)) |
|||
panic(content) |
|||
} |
|||
@ -0,0 +1,634 @@ |
|||
package rest |
|||
|
|||
import ( |
|||
"path/filepath" |
|||
"strings" |
|||
) |
|||
|
|||
//根据文件名字获取后缀名,均是小写。
|
|||
func GetExtension(filename string) string { |
|||
|
|||
var extension = filepath.Ext(filename) |
|||
|
|||
return strings.ToLower(extension) |
|||
|
|||
} |
|||
|
|||
//根据一个后缀名获取MimeType
|
|||
func GetMimeType(filename string) string { |
|||
|
|||
extension := GetExtension(filename) |
|||
|
|||
mimeMap := map[string]string{ |
|||
".323": "text/h323", |
|||
".3g2": "video/3gpp2", |
|||
".3gp": "video/3gpp", |
|||
".3gp2": "video/3gpp2", |
|||
".3gpp": "video/3gpp", |
|||
".7z": "application/x-7z-compressed", |
|||
".aa": "audio/audible", |
|||
".AAC": "audio/aac", |
|||
".aaf": "application/octet-stream", |
|||
".aax": "audio/vnd.audible.aax", |
|||
".ac3": "audio/ac3", |
|||
".aca": "application/octet-stream", |
|||
".accda": "application/msaccess.addin", |
|||
".accdb": "application/msaccess", |
|||
".accdc": "application/msaccess.cab", |
|||
".accde": "application/msaccess", |
|||
".accdr": "application/msaccess.runtime", |
|||
".accdt": "application/msaccess", |
|||
".accdw": "application/msaccess.webapplication", |
|||
".accft": "application/msaccess.ftemplate", |
|||
".acx": "application/internet-property-stream", |
|||
".AddIn": "text/xml", |
|||
".ade": "application/msaccess", |
|||
".adobebridge": "application/x-bridge-url", |
|||
".adp": "application/msaccess", |
|||
".ADT": "audio/vnd.dlna.adts", |
|||
".ADTS": "audio/aac", |
|||
".afm": "application/octet-stream", |
|||
".ai": "application/postscript", |
|||
".aif": "audio/aiff", |
|||
".aifc": "audio/aiff", |
|||
".aiff": "audio/aiff", |
|||
".air": "application/vnd.adobe.air-application-installer-package+zip", |
|||
".amc": "application/mpeg", |
|||
".anx": "application/annodex", |
|||
".apk": "application/vnd.android.package-archive", |
|||
".application": "application/x-ms-application", |
|||
".art": "image/x-jg", |
|||
".asa": "application/xml", |
|||
".asax": "application/xml", |
|||
".ascx": "application/xml", |
|||
".asd": "application/octet-stream", |
|||
".asf": "video/x-ms-asf", |
|||
".ashx": "application/xml", |
|||
".asi": "application/octet-stream", |
|||
".asm": "text/plain", |
|||
".asmx": "application/xml", |
|||
".aspx": "application/xml", |
|||
".asr": "video/x-ms-asf", |
|||
".asx": "video/x-ms-asf", |
|||
".atom": "application/atom+xml", |
|||
".au": "audio/basic", |
|||
".avi": "video/x-msvideo", |
|||
".axa": "audio/annodex", |
|||
".axs": "application/olescript", |
|||
".axv": "video/annodex", |
|||
".bas": "text/plain", |
|||
".bcpio": "application/x-bcpio", |
|||
".bin": "application/octet-stream", |
|||
".bmp": "image/bmp", |
|||
".c": "text/plain", |
|||
".cab": "application/octet-stream", |
|||
".caf": "audio/x-caf", |
|||
".calx": "application/vnd.ms-office.calx", |
|||
".cat": "application/vnd.ms-pki.seccat", |
|||
".cc": "text/plain", |
|||
".cd": "text/plain", |
|||
".cdda": "audio/aiff", |
|||
".cdf": "application/x-cdf", |
|||
".cer": "application/x-x509-ca-cert", |
|||
".cfg": "text/plain", |
|||
".chm": "application/octet-stream", |
|||
".class": "application/x-java-applet", |
|||
".clp": "application/x-msclip", |
|||
".cmd": "text/plain", |
|||
".cmx": "image/x-cmx", |
|||
".cnf": "text/plain", |
|||
".cod": "image/cis-cod", |
|||
".config": "application/xml", |
|||
".contact": "text/x-ms-contact", |
|||
".coverage": "application/xml", |
|||
".cpio": "application/x-cpio", |
|||
".cpp": "text/plain", |
|||
".crd": "application/x-mscardfile", |
|||
".crl": "application/pkix-crl", |
|||
".crt": "application/x-x509-ca-cert", |
|||
".cs": "text/plain", |
|||
".csdproj": "text/plain", |
|||
".csh": "application/x-csh", |
|||
".csproj": "text/plain", |
|||
".css": "text/css", |
|||
".csv": "text/csv", |
|||
".cur": "application/octet-stream", |
|||
".cxx": "text/plain", |
|||
".dat": "application/octet-stream", |
|||
".datasource": "application/xml", |
|||
".dbproj": "text/plain", |
|||
".dcr": "application/x-director", |
|||
".def": "text/plain", |
|||
".deploy": "application/octet-stream", |
|||
".der": "application/x-x509-ca-cert", |
|||
".dgml": "application/xml", |
|||
".dib": "image/bmp", |
|||
".dif": "video/x-dv", |
|||
".dir": "application/x-director", |
|||
".disco": "text/xml", |
|||
".divx": "video/divx", |
|||
".dll": "application/x-msdownload", |
|||
".dll.config": "text/xml", |
|||
".dlm": "text/dlm", |
|||
".doc": "application/msword", |
|||
".docm": "application/vnd.ms-word.document.macroEnabled.12", |
|||
".docx": "application/vnd.openxmlformats-officedocument.wordprocessingml.document", |
|||
".dot": "application/msword", |
|||
".dotm": "application/vnd.ms-word.template.macroEnabled.12", |
|||
".dotx": "application/vnd.openxmlformats-officedocument.wordprocessingml.template", |
|||
".dsp": "application/octet-stream", |
|||
".dsw": "text/plain", |
|||
".dtd": "text/xml", |
|||
".dtsConfig": "text/xml", |
|||
".dv": "video/x-dv", |
|||
".dvi": "application/x-dvi", |
|||
".dwf": "drawing/x-dwf", |
|||
".dwg": "application/acad", |
|||
".dwp": "application/octet-stream", |
|||
".dxf": "application/x-dxf", |
|||
".dxr": "application/x-director", |
|||
".eml": "message/rfc822", |
|||
".emz": "application/octet-stream", |
|||
".eot": "application/vnd.ms-fontobject", |
|||
".eps": "application/postscript", |
|||
".etl": "application/etl", |
|||
".etx": "text/x-setext", |
|||
".evy": "application/envoy", |
|||
".exe": "application/octet-stream", |
|||
".exe.config": "text/xml", |
|||
".fdf": "application/vnd.fdf", |
|||
".fif": "application/fractals", |
|||
".filters": "application/xml", |
|||
".fla": "application/octet-stream", |
|||
".flac": "audio/flac", |
|||
".flr": "x-world/x-vrml", |
|||
".flv": "video/x-flv", |
|||
".fsscript": "application/fsharp-script", |
|||
".fsx": "application/fsharp-script", |
|||
".generictest": "application/xml", |
|||
".gif": "image/gif", |
|||
".gpx": "application/gpx+xml", |
|||
".group": "text/x-ms-group", |
|||
".gsm": "audio/x-gsm", |
|||
".gtar": "application/x-gtar", |
|||
".gz": "application/x-gzip", |
|||
".h": "text/plain", |
|||
".hdf": "application/x-hdf", |
|||
".hdml": "text/x-hdml", |
|||
".hhc": "application/x-oleobject", |
|||
".hhk": "application/octet-stream", |
|||
".hhp": "application/octet-stream", |
|||
".hlp": "application/winhlp", |
|||
".hpp": "text/plain", |
|||
".hqx": "application/mac-binhex40", |
|||
".hta": "application/hta", |
|||
".htc": "text/x-component", |
|||
".htm": "text/html", |
|||
".html": "text/html", |
|||
".htt": "text/webviewhtml", |
|||
".hxa": "application/xml", |
|||
".hxc": "application/xml", |
|||
".hxd": "application/octet-stream", |
|||
".hxe": "application/xml", |
|||
".hxf": "application/xml", |
|||
".hxh": "application/octet-stream", |
|||
".hxi": "application/octet-stream", |
|||
".hxk": "application/xml", |
|||
".hxq": "application/octet-stream", |
|||
".hxr": "application/octet-stream", |
|||
".hxs": "application/octet-stream", |
|||
".hxt": "text/html", |
|||
".hxv": "application/xml", |
|||
".hxw": "application/octet-stream", |
|||
".hxx": "text/plain", |
|||
".i": "text/plain", |
|||
".ico": "image/x-icon", |
|||
".ics": "application/octet-stream", |
|||
".idl": "text/plain", |
|||
".ief": "image/ief", |
|||
".iii": "application/x-iphone", |
|||
".inc": "text/plain", |
|||
".inf": "application/octet-stream", |
|||
".ini": "text/plain", |
|||
".inl": "text/plain", |
|||
".ins": "application/x-internet-signup", |
|||
".ipa": "application/x-itunes-ipa", |
|||
".ipg": "application/x-itunes-ipg", |
|||
".ipproj": "text/plain", |
|||
".ipsw": "application/x-itunes-ipsw", |
|||
".iqy": "text/x-ms-iqy", |
|||
".isp": "application/x-internet-signup", |
|||
".ite": "application/x-itunes-ite", |
|||
".itlp": "application/x-itunes-itlp", |
|||
".itms": "application/x-itunes-itms", |
|||
".itpc": "application/x-itunes-itpc", |
|||
".IVF": "video/x-ivf", |
|||
".jar": "application/java-archive", |
|||
".java": "application/octet-stream", |
|||
".jck": "application/liquidmotion", |
|||
".jcz": "application/liquidmotion", |
|||
".jfif": "image/pjpeg", |
|||
".jnlp": "application/x-java-jnlp-file", |
|||
".jpb": "application/octet-stream", |
|||
".jpe": "image/jpeg", |
|||
".jpeg": "image/jpeg", |
|||
".jpg": "image/jpeg", |
|||
".js": "application/javascript", |
|||
".json": "application/json", |
|||
".jsx": "text/jscript", |
|||
".jsxbin": "text/plain", |
|||
".latex": "application/x-latex", |
|||
".library-ms": "application/windows-library+xml", |
|||
".lit": "application/x-ms-reader", |
|||
".loadtest": "application/xml", |
|||
".lpk": "application/octet-stream", |
|||
".lsf": "video/x-la-asf", |
|||
".lst": "text/plain", |
|||
".lsx": "video/x-la-asf", |
|||
".lzh": "application/octet-stream", |
|||
".m13": "application/x-msmediaview", |
|||
".m14": "application/x-msmediaview", |
|||
".m1v": "video/mpeg", |
|||
".m2t": "video/vnd.dlna.mpeg-tts", |
|||
".m2ts": "video/vnd.dlna.mpeg-tts", |
|||
".m2v": "video/mpeg", |
|||
".m3u": "audio/x-mpegurl", |
|||
".m3u8": "audio/x-mpegurl", |
|||
".m4a": "audio/m4a", |
|||
".m4b": "audio/m4b", |
|||
".m4p": "audio/m4p", |
|||
".m4r": "audio/x-m4r", |
|||
".m4v": "video/x-m4v", |
|||
".mac": "image/x-macpaint", |
|||
".mak": "text/plain", |
|||
".man": "application/x-troff-man", |
|||
".manifest": "application/x-ms-manifest", |
|||
".map": "text/plain", |
|||
".master": "application/xml", |
|||
".mbox": "application/mbox", |
|||
".mda": "application/msaccess", |
|||
".mdb": "application/x-msaccess", |
|||
".mde": "application/msaccess", |
|||
".mdp": "application/octet-stream", |
|||
".me": "application/x-troff-me", |
|||
".mfp": "application/x-shockwave-flash", |
|||
".mht": "message/rfc822", |
|||
".mhtml": "message/rfc822", |
|||
".mid": "audio/mid", |
|||
".midi": "audio/mid", |
|||
".mix": "application/octet-stream", |
|||
".mk": "text/plain", |
|||
".mmf": "application/x-smaf", |
|||
".mno": "text/xml", |
|||
".mny": "application/x-msmoney", |
|||
".mod": "video/mpeg", |
|||
".mov": "video/quicktime", |
|||
".movie": "video/x-sgi-movie", |
|||
".mp2": "video/mpeg", |
|||
".mp2v": "video/mpeg", |
|||
".mp3": "audio/mpeg", |
|||
".mp4": "video/mp4", |
|||
".mp4v": "video/mp4", |
|||
".mpa": "video/mpeg", |
|||
".mpe": "video/mpeg", |
|||
".mpeg": "video/mpeg", |
|||
".mpf": "application/vnd.ms-mediapackage", |
|||
".mpg": "video/mpeg", |
|||
".mpp": "application/vnd.ms-project", |
|||
".mpv2": "video/mpeg", |
|||
".mqv": "video/quicktime", |
|||
".ms": "application/x-troff-ms", |
|||
".msg": "application/vnd.ms-outlook", |
|||
".msi": "application/octet-stream", |
|||
".mso": "application/octet-stream", |
|||
".mts": "video/vnd.dlna.mpeg-tts", |
|||
".mtx": "application/xml", |
|||
".mvb": "application/x-msmediaview", |
|||
".mvc": "application/x-miva-compiled", |
|||
".mxp": "application/x-mmxp", |
|||
".nc": "application/x-netcdf", |
|||
".nsc": "video/x-ms-asf", |
|||
".nws": "message/rfc822", |
|||
".ocx": "application/octet-stream", |
|||
".oda": "application/oda", |
|||
".odb": "application/vnd.oasis.opendocument.database", |
|||
".odc": "application/vnd.oasis.opendocument.chart", |
|||
".odf": "application/vnd.oasis.opendocument.formula", |
|||
".odg": "application/vnd.oasis.opendocument.graphics", |
|||
".odh": "text/plain", |
|||
".odi": "application/vnd.oasis.opendocument.image", |
|||
".odl": "text/plain", |
|||
".odm": "application/vnd.oasis.opendocument.text-master", |
|||
".odp": "application/vnd.oasis.opendocument.presentation", |
|||
".ods": "application/vnd.oasis.opendocument.spreadsheet", |
|||
".odt": "application/vnd.oasis.opendocument.text", |
|||
".oga": "audio/ogg", |
|||
".ogg": "audio/ogg", |
|||
".ogv": "video/ogg", |
|||
".ogx": "application/ogg", |
|||
".one": "application/onenote", |
|||
".onea": "application/onenote", |
|||
".onepkg": "application/onenote", |
|||
".onetmp": "application/onenote", |
|||
".onetoc": "application/onenote", |
|||
".onetoc2": "application/onenote", |
|||
".opus": "audio/ogg", |
|||
".orderedtest": "application/xml", |
|||
".osdx": "application/opensearchdescription+xml", |
|||
".otf": "application/font-sfnt", |
|||
".otg": "application/vnd.oasis.opendocument.graphics-template", |
|||
".oth": "application/vnd.oasis.opendocument.text-web", |
|||
".otp": "application/vnd.oasis.opendocument.presentation-template", |
|||
".ots": "application/vnd.oasis.opendocument.spreadsheet-template", |
|||
".ott": "application/vnd.oasis.opendocument.text-template", |
|||
".oxt": "application/vnd.openofficeorg.extension", |
|||
".p10": "application/pkcs10", |
|||
".p12": "application/x-pkcs12", |
|||
".p7b": "application/x-pkcs7-certificates", |
|||
".p7c": "application/pkcs7-mime", |
|||
".p7m": "application/pkcs7-mime", |
|||
".p7r": "application/x-pkcs7-certreqresp", |
|||
".p7s": "application/pkcs7-signature", |
|||
".pbm": "image/x-portable-bitmap", |
|||
".pcast": "application/x-podcast", |
|||
".pct": "image/pict", |
|||
".pcx": "application/octet-stream", |
|||
".pcz": "application/octet-stream", |
|||
".pdf": "application/pdf", |
|||
".pfb": "application/octet-stream", |
|||
".pfm": "application/octet-stream", |
|||
".pfx": "application/x-pkcs12", |
|||
".pgm": "image/x-portable-graymap", |
|||
".pic": "image/pict", |
|||
".pict": "image/pict", |
|||
".pkgdef": "text/plain", |
|||
".pkgundef": "text/plain", |
|||
".pko": "application/vnd.ms-pki.pko", |
|||
".pls": "audio/scpls", |
|||
".pma": "application/x-perfmon", |
|||
".pmc": "application/x-perfmon", |
|||
".pml": "application/x-perfmon", |
|||
".pmr": "application/x-perfmon", |
|||
".pmw": "application/x-perfmon", |
|||
".png": "image/png", |
|||
".pnm": "image/x-portable-anymap", |
|||
".pnt": "image/x-macpaint", |
|||
".pntg": "image/x-macpaint", |
|||
".pnz": "image/png", |
|||
".pot": "application/vnd.ms-powerpoint", |
|||
".potm": "application/vnd.ms-powerpoint.template.macroEnabled.12", |
|||
".potx": "application/vnd.openxmlformats-officedocument.presentationml.template", |
|||
".ppa": "application/vnd.ms-powerpoint", |
|||
".ppam": "application/vnd.ms-powerpoint.addin.macroEnabled.12", |
|||
".ppm": "image/x-portable-pixmap", |
|||
".pps": "application/vnd.ms-powerpoint", |
|||
".ppsm": "application/vnd.ms-powerpoint.slideshow.macroEnabled.12", |
|||
".ppsx": "application/vnd.openxmlformats-officedocument.presentationml.slideshow", |
|||
".ppt": "application/vnd.ms-powerpoint", |
|||
".pptm": "application/vnd.ms-powerpoint.presentation.macroEnabled.12", |
|||
".pptx": "application/vnd.openxmlformats-officedocument.presentationml.presentation", |
|||
".prf": "application/pics-rules", |
|||
".prm": "application/octet-stream", |
|||
".prx": "application/octet-stream", |
|||
".ps": "application/postscript", |
|||
".psc1": "application/PowerShell", |
|||
".psd": "application/octet-stream", |
|||
".psess": "application/xml", |
|||
".psm": "application/octet-stream", |
|||
".psp": "application/octet-stream", |
|||
".pst": "application/vnd.ms-outlook", |
|||
".pub": "application/x-mspublisher", |
|||
".pwz": "application/vnd.ms-powerpoint", |
|||
".qht": "text/x-html-insertion", |
|||
".qhtm": "text/x-html-insertion", |
|||
".qt": "video/quicktime", |
|||
".qti": "image/x-quicktime", |
|||
".qtif": "image/x-quicktime", |
|||
".qtl": "application/x-quicktimeplayer", |
|||
".qxd": "application/octet-stream", |
|||
".ra": "audio/x-pn-realaudio", |
|||
".ram": "audio/x-pn-realaudio", |
|||
".rar": "application/x-rar-compressed", |
|||
".ras": "image/x-cmu-raster", |
|||
".rat": "application/rat-file", |
|||
".rc": "text/plain", |
|||
".rc2": "text/plain", |
|||
".rct": "text/plain", |
|||
".rdlc": "application/xml", |
|||
".reg": "text/plain", |
|||
".resx": "application/xml", |
|||
".rf": "image/vnd.rn-realflash", |
|||
".rgb": "image/x-rgb", |
|||
".rgs": "text/plain", |
|||
".rm": "application/vnd.rn-realmedia", |
|||
".rmi": "audio/mid", |
|||
".rmp": "application/vnd.rn-rn_music_package", |
|||
".roff": "application/x-troff", |
|||
".rpm": "audio/x-pn-realaudio-plugin", |
|||
".rqy": "text/x-ms-rqy", |
|||
".rtf": "application/rtf", |
|||
".rtx": "text/richtext", |
|||
".rvt": "application/octet-stream", |
|||
".ruleset": "application/xml", |
|||
".s": "text/plain", |
|||
".safariextz": "application/x-safari-safariextz", |
|||
".scd": "application/x-msschedule", |
|||
".scr": "text/plain", |
|||
".sct": "text/scriptlet", |
|||
".sd2": "audio/x-sd2", |
|||
".sdp": "application/sdp", |
|||
".sea": "application/octet-stream", |
|||
".searchConnector-ms": "application/windows-search-connector+xml", |
|||
".setpay": "application/set-payment-initiation", |
|||
".setreg": "application/set-registration-initiation", |
|||
".settings": "application/xml", |
|||
".sgimb": "application/x-sgimb", |
|||
".sgml": "text/sgml", |
|||
".sh": "application/x-sh", |
|||
".shar": "application/x-shar", |
|||
".shtml": "text/html", |
|||
".sit": "application/x-stuffit", |
|||
".sitemap": "application/xml", |
|||
".skin": "application/xml", |
|||
".skp": "application/x-koan", |
|||
".sldm": "application/vnd.ms-powerpoint.slide.macroEnabled.12", |
|||
".sldx": "application/vnd.openxmlformats-officedocument.presentationml.slide", |
|||
".slk": "application/vnd.ms-excel", |
|||
".sln": "text/plain", |
|||
".slupkg-ms": "application/x-ms-license", |
|||
".smd": "audio/x-smd", |
|||
".smi": "application/octet-stream", |
|||
".smx": "audio/x-smd", |
|||
".smz": "audio/x-smd", |
|||
".snd": "audio/basic", |
|||
".snippet": "application/xml", |
|||
".snp": "application/octet-stream", |
|||
".sol": "text/plain", |
|||
".sor": "text/plain", |
|||
".spc": "application/x-pkcs7-certificates", |
|||
".spl": "application/futuresplash", |
|||
".spx": "audio/ogg", |
|||
".src": "application/x-wais-source", |
|||
".srf": "text/plain", |
|||
".SSISDeploymentManifest": "text/xml", |
|||
".ssm": "application/streamingmedia", |
|||
".sst": "application/vnd.ms-pki.certstore", |
|||
".stl": "application/vnd.ms-pki.stl", |
|||
".sv4cpio": "application/x-sv4cpio", |
|||
".sv4crc": "application/x-sv4crc", |
|||
".svc": "application/xml", |
|||
".svg": "image/svg+xml", |
|||
".swf": "application/x-shockwave-flash", |
|||
".step": "application/step", |
|||
".stp": "application/step", |
|||
".t": "application/x-troff", |
|||
".tar": "application/x-tar", |
|||
".tcl": "application/x-tcl", |
|||
".testrunconfig": "application/xml", |
|||
".testsettings": "application/xml", |
|||
".tex": "application/x-tex", |
|||
".texi": "application/x-texinfo", |
|||
".texinfo": "application/x-texinfo", |
|||
".tgz": "application/x-compressed", |
|||
".thmx": "application/vnd.ms-officetheme", |
|||
".thn": "application/octet-stream", |
|||
".tif": "image/tiff", |
|||
".tiff": "image/tiff", |
|||
".tlh": "text/plain", |
|||
".tli": "text/plain", |
|||
".toc": "application/octet-stream", |
|||
".tr": "application/x-troff", |
|||
".trm": "application/x-msterminal", |
|||
".trx": "application/xml", |
|||
".ts": "video/vnd.dlna.mpeg-tts", |
|||
".tsv": "text/tab-separated-values", |
|||
".ttf": "application/font-sfnt", |
|||
".tts": "video/vnd.dlna.mpeg-tts", |
|||
".txt": "text/plain", |
|||
".u32": "application/octet-stream", |
|||
".uls": "text/iuls", |
|||
".user": "text/plain", |
|||
".ustar": "application/x-ustar", |
|||
".vb": "text/plain", |
|||
".vbdproj": "text/plain", |
|||
".vbk": "video/mpeg", |
|||
".vbproj": "text/plain", |
|||
".vbs": "text/vbscript", |
|||
".vcf": "text/x-vcard", |
|||
".vcproj": "application/xml", |
|||
".vcs": "text/plain", |
|||
".vcxproj": "application/xml", |
|||
".vddproj": "text/plain", |
|||
".vdp": "text/plain", |
|||
".vdproj": "text/plain", |
|||
".vdx": "application/vnd.ms-visio.viewer", |
|||
".vml": "text/xml", |
|||
".vscontent": "application/xml", |
|||
".vsct": "text/xml", |
|||
".vsd": "application/vnd.visio", |
|||
".vsi": "application/ms-vsi", |
|||
".vsix": "application/vsix", |
|||
".vsixlangpack": "text/xml", |
|||
".vsixmanifest": "text/xml", |
|||
".vsmdi": "application/xml", |
|||
".vspscc": "text/plain", |
|||
".vss": "application/vnd.visio", |
|||
".vsscc": "text/plain", |
|||
".vssettings": "text/xml", |
|||
".vssscc": "text/plain", |
|||
".vst": "application/vnd.visio", |
|||
".vstemplate": "text/xml", |
|||
".vsto": "application/x-ms-vsto", |
|||
".vsw": "application/vnd.visio", |
|||
".vsx": "application/vnd.visio", |
|||
".vtx": "application/vnd.visio", |
|||
".wav": "audio/wav", |
|||
".wave": "audio/wav", |
|||
".wax": "audio/x-ms-wax", |
|||
".wbk": "application/msword", |
|||
".wbmp": "image/vnd.wap.wbmp", |
|||
".wcm": "application/vnd.ms-works", |
|||
".wdb": "application/vnd.ms-works", |
|||
".wdp": "image/vnd.ms-photo", |
|||
".webarchive": "application/x-safari-webarchive", |
|||
".webm": "video/webm", |
|||
".webp": "image/webp", /* https"://en.wikipedia.org/wiki/WebP */ |
|||
".webtest": "application/xml", |
|||
".wiq": "application/xml", |
|||
".wiz": "application/msword", |
|||
".wks": "application/vnd.ms-works", |
|||
".WLMP": "application/wlmoviemaker", |
|||
".wlpginstall": "application/x-wlpg-detect", |
|||
".wlpginstall3": "application/x-wlpg3-detect", |
|||
".wm": "video/x-ms-wm", |
|||
".wma": "audio/x-ms-wma", |
|||
".wmd": "application/x-ms-wmd", |
|||
".wmf": "application/x-msmetafile", |
|||
".wml": "text/vnd.wap.wml", |
|||
".wmlc": "application/vnd.wap.wmlc", |
|||
".wmls": "text/vnd.wap.wmlscript", |
|||
".wmlsc": "application/vnd.wap.wmlscriptc", |
|||
".wmp": "video/x-ms-wmp", |
|||
".wmv": "video/x-ms-wmv", |
|||
".wmx": "video/x-ms-wmx", |
|||
".wmz": "application/x-ms-wmz", |
|||
".woff": "application/font-woff", |
|||
".wpl": "application/vnd.ms-wpl", |
|||
".wps": "application/vnd.ms-works", |
|||
".wri": "application/x-mswrite", |
|||
".wrl": "x-world/x-vrml", |
|||
".wrz": "x-world/x-vrml", |
|||
".wsc": "text/scriptlet", |
|||
".wsdl": "text/xml", |
|||
".wvx": "video/x-ms-wvx", |
|||
".x": "application/directx", |
|||
".xaf": "x-world/x-vrml", |
|||
".xaml": "application/xaml+xml", |
|||
".xap": "application/x-silverlight-app", |
|||
".xbap": "application/x-ms-xbap", |
|||
".xbm": "image/x-xbitmap", |
|||
".xdr": "text/plain", |
|||
".xht": "application/xhtml+xml", |
|||
".xhtml": "application/xhtml+xml", |
|||
".xla": "application/vnd.ms-excel", |
|||
".xlam": "application/vnd.ms-excel.addin.macroEnabled.12", |
|||
".xlc": "application/vnd.ms-excel", |
|||
".xld": "application/vnd.ms-excel", |
|||
".xlk": "application/vnd.ms-excel", |
|||
".xll": "application/vnd.ms-excel", |
|||
".xlm": "application/vnd.ms-excel", |
|||
".xls": "application/vnd.ms-excel", |
|||
".xlsb": "application/vnd.ms-excel.sheet.binary.macroEnabled.12", |
|||
".xlsm": "application/vnd.ms-excel.sheet.macroEnabled.12", |
|||
".xlsx": "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", |
|||
".xlt": "application/vnd.ms-excel", |
|||
".xltm": "application/vnd.ms-excel.template.macroEnabled.12", |
|||
".xltx": "application/vnd.openxmlformats-officedocument.spreadsheetml.template", |
|||
".xlw": "application/vnd.ms-excel", |
|||
".xml": "text/xml", |
|||
".xmp": "application/octet-stream", |
|||
".xmta": "application/xml", |
|||
".xof": "x-world/x-vrml", |
|||
".XOML": "text/plain", |
|||
".xpm": "image/x-xpixmap", |
|||
".xps": "application/vnd.ms-xpsdocument", |
|||
".xrm-ms": "text/xml", |
|||
".xsc": "application/xml", |
|||
".xsd": "text/xml", |
|||
".xsf": "text/xml", |
|||
".xsl": "text/xml", |
|||
".xslt": "text/xml", |
|||
".xsn": "application/octet-stream", |
|||
".xss": "application/xml", |
|||
".xspf": "application/xspf+xml", |
|||
".xtp": "application/octet-stream", |
|||
".xwd": "image/x-xwindowdump", |
|||
".z": "application/x-compress", |
|||
".zip": "application/zip"} |
|||
|
|||
if mimeType, ok := mimeMap[extension]; ok { |
|||
return mimeType |
|||
} else { |
|||
return "application/octet-stream" |
|||
} |
|||
} |
|||
@ -0,0 +1,26 @@ |
|||
package rest |
|||
|
|||
import ( |
|||
"net/http" |
|||
"strings" |
|||
) |
|||
|
|||
//根据一个请求,获取ip.
|
|||
func GetIpAddress(r *http.Request) string { |
|||
var ipAddress string |
|||
|
|||
ipAddress = r.RemoteAddr |
|||
|
|||
if ipAddress != "" { |
|||
ipAddress = strings.Split(ipAddress, ":")[0] |
|||
} |
|||
|
|||
for _, h := range []string{"X-Forwarded-For", "X-Real-Ip"} { |
|||
for _, ip := range strings.Split(r.Header.Get(h), ",") { |
|||
if ip != "" { |
|||
ipAddress = ip |
|||
} |
|||
} |
|||
} |
|||
return ipAddress |
|||
} |
|||
@ -0,0 +1,138 @@ |
|||
package rest |
|||
|
|||
import ( |
|||
"fmt" |
|||
"os" |
|||
"time" |
|||
"path/filepath" |
|||
) |
|||
|
|||
//判断文件或文件夹是否已经存在
|
|||
func PathExists(path string) (bool, error) { |
|||
_, err := os.Stat(path) |
|||
if err == nil { |
|||
return true, nil |
|||
} |
|||
if os.IsNotExist(err) { |
|||
return false, nil |
|||
} |
|||
return false, err |
|||
} |
|||
|
|||
//获取该应用可执行文件的位置。
|
|||
//例如:C:\Users\lishuang\AppData\Local\Temp
|
|||
func GetHomePath() string { |
|||
ex, err := os.Executable() |
|||
if err != nil { |
|||
panic(err) |
|||
} |
|||
exPath := filepath.Dir(ex) |
|||
return exPath |
|||
} |
|||
|
|||
//获取前端静态资源的位置。如果你在开发模式下,可以将这里直接返回tank/build下面的html路径。
|
|||
//例如:C:/Users/lishuang/AppData/Local/Temp/html
|
|||
func GetHtmlPath() string { |
|||
|
|||
homePath := GetHomePath() |
|||
filePath := homePath + "/html" |
|||
exists, err := PathExists(filePath) |
|||
if err != nil { |
|||
panic("判断上传文件是否存在时出错!") |
|||
} |
|||
if !exists { |
|||
err = os.MkdirAll(filePath, 0777) |
|||
if err != nil { |
|||
panic("创建上传文件夹时出错!") |
|||
} |
|||
} |
|||
|
|||
return filePath |
|||
} |
|||
|
|||
//获取上传文件存放的位置。
|
|||
//例如:C:\Users\lishuang\AppData\Local\Temp/matter
|
|||
func GetFilePath() string { |
|||
|
|||
homePath := GetHomePath() |
|||
filePath := homePath + "/matter" |
|||
exists, err := PathExists(filePath) |
|||
if err != nil { |
|||
panic("判断上传文件是否存在时出错!") |
|||
} |
|||
if !exists { |
|||
err = os.MkdirAll(filePath, 0777) |
|||
if err != nil { |
|||
panic("创建上传文件夹时出错!") |
|||
} |
|||
} |
|||
|
|||
return filePath |
|||
} |
|||
|
|||
//获取日志存放的位置。
|
|||
//例如:C:\Users\lishuang\AppData\Local\Temp/log
|
|||
func GetLogPath() string { |
|||
|
|||
homePath := GetHomePath() |
|||
filePath := homePath + "/log" |
|||
exists, err := PathExists(filePath) |
|||
if err != nil { |
|||
panic("判断日志文件夹是否存在时出错!") |
|||
} |
|||
if !exists { |
|||
err = os.MkdirAll(filePath, 0666) |
|||
if err != nil { |
|||
panic("创建日志文件夹时出错!") |
|||
} |
|||
} |
|||
|
|||
return filePath |
|||
} |
|||
|
|||
//获取配置文件存放的位置
|
|||
//例如:C:\Users\lishuang\AppData\Local\Temp/conf
|
|||
func GetConfPath() string { |
|||
|
|||
homePath := GetHomePath() |
|||
filePath := homePath + "/conf" |
|||
exists, err := PathExists(filePath) |
|||
if err != nil { |
|||
panic("判断日志文件夹是否存在时出错!") |
|||
} |
|||
if !exists { |
|||
err = os.MkdirAll(filePath, 0666) |
|||
if err != nil { |
|||
panic("创建日志文件夹时出错!") |
|||
} |
|||
} |
|||
|
|||
return filePath |
|||
} |
|||
|
|||
//获取某个用户文件应该存放的位置。这个是相对GetFilePath的路径
|
|||
//例如:/zicla/2006-01-02/1510122428000
|
|||
func GetUserFilePath(username string) (string, string) { |
|||
|
|||
now := time.Now() |
|||
datePath := now.Format("2006-01-02") |
|||
//毫秒时间戳
|
|||
timestamp := now.UnixNano() / 1e6 |
|||
|
|||
filePath := GetFilePath() |
|||
absolutePath := fmt.Sprintf("%s/%s/%s/%d", filePath, username, datePath, timestamp) |
|||
relativePath := fmt.Sprintf("/%s/%s/%d", username, datePath, timestamp) |
|||
|
|||
exists, err := PathExists(absolutePath) |
|||
if err != nil { |
|||
panic("判断上传文件是否存在时出错!") |
|||
} |
|||
if !exists { |
|||
err = os.MkdirAll(absolutePath, 0777) |
|||
if err != nil { |
|||
panic("创建上传文件夹时出错!") |
|||
} |
|||
} |
|||
|
|||
return absolutePath, relativePath |
|||
} |
|||
@ -0,0 +1,115 @@ |
|||
package rest |
|||
|
|||
type WebResult struct { |
|||
Code int `json:"code"` |
|||
Msg string `json:"msg"` |
|||
Data interface{} `json:"data"` |
|||
} |
|||
|
|||
func (this *WebResult) Error() string { |
|||
return this.Msg |
|||
} |
|||
|
|||
const ( |
|||
//正常
|
|||
RESULT_CODE_OK = 200 |
|||
|
|||
//未登录
|
|||
RESULT_CODE_LOGIN = -400 |
|||
|
|||
//没有权限
|
|||
RESULT_CODE_UNAUTHORIZED = -401 |
|||
|
|||
//请求错误
|
|||
RESULT_CODE_BAD_REQUEST = -402 |
|||
|
|||
//没有找到
|
|||
RESULT_CODE_NOT_FOUND = -404 |
|||
|
|||
//登录过期
|
|||
RESULT_CODE_LOGIN_EXPIRED = -405 |
|||
|
|||
//该登录用户不是有效用户
|
|||
RESULT_CODE_LOGIN_INVALID = -406 |
|||
|
|||
//提交的表单验证不通过
|
|||
RESULT_CODE_FORM_INVALID = -410 |
|||
|
|||
//请求太频繁
|
|||
RESULT_CODE_FREQUENCY = -420 |
|||
|
|||
//服务器出错。
|
|||
RESULT_CODE_SERVER_ERROR = -500 |
|||
|
|||
//远程服务不可用
|
|||
RESULT_CODE_NOT_AVAILABLE = -501 |
|||
|
|||
//并发异常
|
|||
RESULT_CODE_CONCURRENCY = -511 |
|||
|
|||
//远程微服务没有找到
|
|||
RESULT_CODE_SERVICE_NOT_FOUND = -600 |
|||
|
|||
//远程微服务连接超时
|
|||
RESULT_CODE_SERVICE_TIME_OUT = -610 |
|||
|
|||
//通用的异常
|
|||
RESULT_CODE_UTIL_EXCEPTION = -700 |
|||
) |
|||
|
|||
func ConstWebResult(code int) *WebResult { |
|||
|
|||
wr := &WebResult{} |
|||
switch code { |
|||
//正常
|
|||
case RESULT_CODE_OK: |
|||
wr.Msg = "成功" |
|||
//未登录
|
|||
case RESULT_CODE_LOGIN: |
|||
wr.Msg = "没有登录,禁止访问" |
|||
//没有权限
|
|||
case RESULT_CODE_UNAUTHORIZED: |
|||
wr.Msg = "没有权限" |
|||
//请求错误
|
|||
case RESULT_CODE_BAD_REQUEST: |
|||
wr.Msg = "请求错误" |
|||
//没有找到
|
|||
case RESULT_CODE_NOT_FOUND: |
|||
wr.Msg = "没有找到" |
|||
//登录过期
|
|||
case RESULT_CODE_LOGIN_EXPIRED: |
|||
wr.Msg = "登录过期" |
|||
|
|||
//该登录用户不是有效用户
|
|||
case RESULT_CODE_LOGIN_INVALID: |
|||
wr.Msg = "该登录用户不是有效用户" |
|||
|
|||
//提交的表单验证不通过
|
|||
case RESULT_CODE_FORM_INVALID: |
|||
wr.Msg = "提交的表单验证不通过" |
|||
//请求太频繁
|
|||
case RESULT_CODE_FREQUENCY: |
|||
wr.Msg = "请求太频繁" |
|||
//服务器出错。
|
|||
case RESULT_CODE_SERVER_ERROR: |
|||
wr.Msg = "服务器出错" |
|||
//远程服务不可用
|
|||
case RESULT_CODE_NOT_AVAILABLE: |
|||
wr.Msg = "远程服务不可用" |
|||
//并发异常
|
|||
case RESULT_CODE_CONCURRENCY: |
|||
wr.Msg = "并发异常" |
|||
//远程微服务没有找到
|
|||
case RESULT_CODE_SERVICE_NOT_FOUND: |
|||
wr.Msg = "远程微服务没有找到" |
|||
//远程微服务连接超时
|
|||
case RESULT_CODE_SERVICE_TIME_OUT: |
|||
wr.Msg = "远程微服务连接超时" |
|||
default: |
|||
code = RESULT_CODE_UTIL_EXCEPTION |
|||
wr.Msg = "服务器未知错误" |
|||
} |
|||
wr.Code = code |
|||
return wr |
|||
|
|||
} |
|||
Loading…
Reference in new issue