├── .gitattributes ├── .gitignore ├── Dockerfile ├── README.md ├── db └── init_db.sql ├── docker-compose.yml ├── docs ├── ReadME.md └── img │ ├── 1.gif │ ├── index.jpg │ ├── login.jpg │ ├── logo.png │ └── show.gif ├── src ├── cmd │ └── main.go ├── common │ ├── config.go │ ├── file.go │ ├── mysql.go │ ├── qetag.go │ └── util.go ├── dao │ ├── IFileDao.go │ └── IUserDao.go ├── datamodels │ ├── FileModel.go │ ├── FileShareModel.go │ ├── RespModel.go │ ├── UserFileModel.go │ ├── UserFileShareModel.go │ └── UserModel.go ├── go.mod ├── go.sum ├── internal │ └── app.go ├── services │ ├── FileService_test.go │ ├── IFileService.go │ ├── IIndexService.go │ └── ILoginService.go └── web │ ├── assets │ ├── css │ │ ├── fonts │ │ │ ├── element-icons.ttf │ │ │ └── element-icons.woff │ │ ├── index.css │ │ ├── login.css │ │ └── login.min.css │ ├── img │ │ ├── fav.ico │ │ ├── logo.svg │ │ ├── logo1.svg │ │ └── t.png │ ├── js │ │ ├── app.js │ │ ├── axios.min.js │ │ ├── index.js │ │ ├── login.js │ │ ├── share.js │ │ └── vue.js │ └── libs │ │ ├── clipboard │ │ ├── clipboard.js │ │ └── clipboard.min.js │ │ ├── flow │ │ ├── flow.js │ │ └── flow.min.js │ │ ├── qetag │ │ ├── qetag.js │ │ └── sha1.min.js │ │ └── safari │ │ └── arrayBuffer.js │ ├── controllers │ ├── FileController.go │ ├── IndexController.go │ ├── LoginController.go │ └── ShareController.go │ ├── middleware │ └── auth_middleware.go │ └── views │ ├── error │ └── error.html │ ├── index │ └── index.html │ ├── login │ └── login.html │ ├── share │ └── share.html │ └── shared │ ├── layout.fw.html │ └── layout.html └── start.sh /.gitattributes: -------------------------------------------------------------------------------- 1 | *.js linguist-language=Go 2 | *.css linguist-language=Go 3 | *.html linguist-language=Go 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Binaries for programs and plugins 2 | *.exe 3 | *.exe~ 4 | *.dll 5 | *.so 6 | *.dylib 7 | 8 | # Test binary, built with `go test -c` 9 | *.test 10 | 11 | # Output of the go coverage tool, specifically when used with LiteIDE 12 | *.out 13 | 14 | # Dependency directories (remove the comment below to include it) 15 | # vendor/ 16 | 17 | 18 | # log 19 | *.log 20 | log 21 | 22 | #.DS_Store 23 | .DS_Store 24 | 25 | .idea 26 | uploads/ 27 | 28 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | # build golang builder 2 | FROM golang:alpine as builder 3 | 4 | RUN apk update 5 | 6 | RUN mkdir /app 7 | WORKDIR /app 8 | COPY src/go.mod . 9 | COPY src/go.sum . 10 | 11 | RUN go mod download 12 | COPY src . 13 | 14 | RUN go build -o /main ./cmd/main.go 15 | 16 | 17 | # running built service 18 | FROM alpine:3.9 19 | 20 | RUN apk add tzdata && cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime \ 21 | && echo "Asia/Shanghai" > /etc/timezone \ 22 | && apk del tzdata 23 | 24 | COPY --from=builder /main . 25 | COPY --from=builder /app/web ./web 26 | 27 | ENTRYPOINT ["/main"] -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![](./docs/img/logo.png) 2 | 3 | --- 4 | 5 | ### 一个支持快速搭建支持私有化部署的个人私有云系统,又可以作为企业级分布式云存储解决方案。 6 | 7 | ![](./docs/img/show.gif) 8 | 9 | ## 功能特点(迭代中ing): 10 | * [x] 依托于golang开发,支持各种平台部署 11 | * [x] 前端分片上传、秒传文件 12 | * [x] 下载支持分片、断点续传,轻松跑满带宽 13 | * [x] 支持文件分享、快速转存,以及安全验证,轻松分享自己的资源 14 | * [ ] 后端存储OSS(阿里云)、KODO(七牛云)、Ceph(私有分布式/aws)等 15 | 16 | ## 使用docker运行(docker-compose) 17 | _推荐将/uploads 挂载到你的本地目录,来保存真实的文件_ 18 | 19 | mac OS, Linux 在根目录下运行 20 | ``` 21 | $ ./start.sh 22 | ``` 23 | 24 | Windows 推荐使用git bash 在根目录下运行 25 | 如果失败,请手动运行 26 | ``` 27 | $ start.sh 28 | ``` 29 | 30 | ## 自行搭建运行 31 | 32 | 1.git clone 33 | ``` 34 | git clone git@github.com:Code-Fight/GoCloud.git 35 | ``` 36 | 37 | 2.go build 38 | ``` 39 | cd GoCloud 40 | go build cmd/main.go 41 | ``` 42 | 43 | 3.init database 44 | ``` 45 | 将 db 目录下的 init_db.sql 在您的mysql数据库下执行 46 | ``` 47 | 48 | -------------------------------------------------------------------------------- /db/init_db.sql: -------------------------------------------------------------------------------- 1 | SET NAMES utf8mb4; 2 | SET FOREIGN_KEY_CHECKS = 0; 3 | 4 | CREATE DATABASE IF NOT EXISTS `pan`; 5 | USE `pan`; 6 | 7 | -- ---------------------------- 8 | -- Table structure for `tbl_file` 9 | -- ---------------------------- 10 | DROP TABLE IF EXISTS `tbl_file`; 11 | CREATE TABLE `tbl_file` ( 12 | `id` int(11) NOT NULL AUTO_INCREMENT, 13 | `file_qetag` char(40) DEFAULT '' COMMENT '文件qetag', 14 | `file_name` varchar(256) NOT NULL DEFAULT '' COMMENT '文件名', 15 | `file_size` bigint(20) DEFAULT '0' COMMENT '文件大小', 16 | `file_addr` varchar(1024) NOT NULL DEFAULT '' COMMENT '文件存储位置', 17 | `create_at` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建日期', 18 | `update_at` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新日期', 19 | `status` int(11) DEFAULT NULL COMMENT '状态(1可用/2禁用/3已删除)', 20 | `ext1` int(11) DEFAULT '0' COMMENT '备用字段1', 21 | `ext2` text COMMENT '备用字段2', 22 | PRIMARY KEY (`id`), 23 | UNIQUE KEY `idx_file_hash` (`file_qetag`), 24 | KEY `idx_status` (`status`) 25 | ) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8; 26 | 27 | -- ---------------------------- 28 | -- Table structure for `tbl_user` 29 | -- ---------------------------- 30 | DROP TABLE IF EXISTS `tbl_user`; 31 | CREATE TABLE `tbl_user` ( 32 | `id` int(11) NOT NULL AUTO_INCREMENT, 33 | `user_name` varchar(64) NOT NULL DEFAULT '' COMMENT '用户名', 34 | `user_pwd` varchar(256) NOT NULL DEFAULT '' COMMENT '用户encoded密码', 35 | `email` varchar(64) DEFAULT '' COMMENT '邮箱', 36 | `phone` varchar(128) DEFAULT '' COMMENT '手机号', 37 | `email_validated` tinyint(1) DEFAULT '0' COMMENT '邮箱是否已验证', 38 | `phone_validated` tinyint(1) DEFAULT '0' COMMENT '手机号是否已验证', 39 | `signup_at` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '注册日期', 40 | `last_active` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '最后活跃时间戳', 41 | `profile` text COMMENT '用户属性', 42 | `status` int(11) NOT NULL DEFAULT '0' COMMENT '账户状态(启用/禁用/锁定/标记删除等)', 43 | PRIMARY KEY (`id`), 44 | UNIQUE KEY `idx_username` (`user_name`), 45 | KEY `idx_status` (`status`) 46 | ) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4; 47 | 48 | -- ---------------------------- 49 | -- Table structure for `tbl_user_file` 50 | -- ---------------------------- 51 | DROP TABLE IF EXISTS `tbl_user_file`; 52 | CREATE TABLE `tbl_user_file` ( 53 | `id` int(11) NOT NULL AUTO_INCREMENT, 54 | `user_name` varchar(64) NOT NULL, 55 | `file_qetag` char(40) DEFAULT '' COMMENT '文件qetag', 56 | `file_size` bigint(20) DEFAULT '0' COMMENT '文件大小', 57 | `file_name` varchar(256) NOT NULL DEFAULT '' COMMENT '文件名', 58 | `is_dir` int(11) DEFAULT '0' COMMENT '1是目录0是文件', 59 | `parent_dir` int(11) DEFAULT '0' COMMENT '父目录', 60 | `upload_at` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '上传时间', 61 | `last_update` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '最后修改时间', 62 | `status` int(11) DEFAULT '1' COMMENT '状态(1可用/2禁用/3已删除)', 63 | PRIMARY KEY (`id`), 64 | UNIQUE KEY `idx_user_file_dir` (`user_name`,`file_qetag`,`parent_dir`), 65 | KEY `idx_status` (`status`), 66 | KEY `idx_user_id` (`user_name`) 67 | ) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4; 68 | 69 | -- ---------------------------- 70 | -- Table structure for `tbl_user_share_file` 71 | -- ---------------------------- 72 | DROP TABLE IF EXISTS `tbl_user_share_file`; 73 | CREATE TABLE `tbl_user_share_file` ( 74 | `id` bigint(20) NOT NULL AUTO_INCREMENT, 75 | `share_id` char(20) DEFAULT '' COMMENT '文件share id(主键的62进制)', 76 | `user_file_id` int(11) NOT NULL COMMENT '用户文件主键', 77 | `create_at` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '分享时间', 78 | `share_pwd` varchar(50) NOT NULL DEFAULT '' COMMENT '文件分享密码', 79 | `share_time` int(11) DEFAULT '7' COMMENT '文件分享时长(1-1天/7-7天/0-永久)', 80 | PRIMARY KEY (`id`), 81 | KEY `share_id` (`share_id`) 82 | ) ENGINE=InnoDB AUTO_INCREMENT=10000000000 DEFAULT CHARSET=utf8mb4; 83 | 84 | SET FOREIGN_KEY_CHECKS = 1; 85 | 86 | 87 | 88 | 89 | USE mysql; 90 | create user 'gocloud'@'%' identified by 'gocloud'; 91 | grant all privileges on pan.* to gocloud@'%' identified by 'gocloud'; 92 | FLUSH PRIVILEGES; -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: "3" 2 | services: 3 | gocloud: 4 | build: . 5 | ports: 6 | - 8080:8080 7 | container_name: "gocloud-app" 8 | links: 9 | - "db:database" 10 | depends_on: 11 | - db 12 | 13 | 14 | db: 15 | restart: always 16 | image: "docker.io/mysql:5.7.30" 17 | container_name: "gocloud-db" 18 | command: 19 | --lower_case_table_names=1 20 | --init-file='/dbfile/init_db.sql' 21 | environment: 22 | TZ: Asia/Shanghai 23 | MYSQL_ALLOW_EMPTY_PASSWORD: "yes" 24 | 25 | ports: 26 | - 23306:3306 27 | volumes: 28 | - "./db:/dbfile/" -------------------------------------------------------------------------------- /docs/ReadME.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Code-Fight/GoCloud/c4a794c588e17a9ca66715c284cd5ec1161d2d9f/docs/ReadME.md -------------------------------------------------------------------------------- /docs/img/1.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Code-Fight/GoCloud/c4a794c588e17a9ca66715c284cd5ec1161d2d9f/docs/img/1.gif -------------------------------------------------------------------------------- /docs/img/index.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Code-Fight/GoCloud/c4a794c588e17a9ca66715c284cd5ec1161d2d9f/docs/img/index.jpg -------------------------------------------------------------------------------- /docs/img/login.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Code-Fight/GoCloud/c4a794c588e17a9ca66715c284cd5ec1161d2d9f/docs/img/login.jpg -------------------------------------------------------------------------------- /docs/img/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Code-Fight/GoCloud/c4a794c588e17a9ca66715c284cd5ec1161d2d9f/docs/img/logo.png -------------------------------------------------------------------------------- /docs/img/show.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Code-Fight/GoCloud/c4a794c588e17a9ca66715c284cd5ec1161d2d9f/docs/img/show.gif -------------------------------------------------------------------------------- /src/cmd/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "gocloud/internal" 4 | 5 | func main() { 6 | internal.AppRun() 7 | } -------------------------------------------------------------------------------- /src/common/config.go: -------------------------------------------------------------------------------- 1 | package common 2 | 3 | import ( 4 | "os" 5 | ) 6 | 7 | var( 8 | User_Pwd_Sha1_Salt = "(*32%$#" 9 | Local_Storage_Mount = "" 10 | ) 11 | 12 | func init() { 13 | var mounterr error 14 | Local_Storage_Mount,mounterr = os.Getwd() 15 | Local_Storage_Mount +="/uploads/" 16 | if mounterr !=nil{ 17 | panic("The Local_Storage_Mount ERROR") 18 | } 19 | } 20 | 21 | -------------------------------------------------------------------------------- /src/common/file.go: -------------------------------------------------------------------------------- 1 | package common 2 | 3 | import "os" 4 | 5 | 6 | // Exists return true if the path exist else return false 7 | func Exists(path string) bool { 8 | _, err := os.Stat(path) //os.Stat获取文件信息 9 | if err != nil { 10 | if os.IsExist(err) { 11 | return true 12 | } 13 | return false 14 | } 15 | return true 16 | } 17 | 18 | func MergeFile() { 19 | 20 | } 21 | 22 | 23 | -------------------------------------------------------------------------------- /src/common/mysql.go: -------------------------------------------------------------------------------- 1 | package common 2 | 3 | import ( 4 | "database/sql" 5 | "errors" 6 | _ "github.com/go-sql-driver/mysql" 7 | "reflect" 8 | "strconv" 9 | "time" 10 | ) 11 | 12 | //创建mysql 连接 13 | func NewMysqlConn() (db *sql.DB, err error) { 14 | db, err = sql.Open("mysql", "gocloud:gocloud@tcp(db:3306)/pan?charset=utf8") 15 | return 16 | } 17 | 18 | //获取返回值,获取一条 19 | func GetResultRow(rows *sql.Rows) map[string]string { 20 | columns, _ := rows.Columns() 21 | scanArgs := make([]interface{}, len(columns)) 22 | values := make([][]byte, len(columns)) 23 | for j := range values { 24 | scanArgs[j] = &values[j] 25 | } 26 | record := make(map[string]string) 27 | for rows.Next() { 28 | //将行数据保存到record字典 29 | rows.Scan(scanArgs...) 30 | for i, v := range values { 31 | if v != nil { 32 | //fmt.Println(reflect.TypeOf(col)) 33 | record[columns[i]] = string(v) 34 | } 35 | } 36 | } 37 | return record 38 | } 39 | 40 | //获取所有 41 | func GetResultRows(rows *sql.Rows) map[int]map[string]string { 42 | //返回所有列 43 | columns, _ := rows.Columns() 44 | //这里表示一行所有列的值,用[]byte表示 45 | vals := make([][]byte, len(columns)) 46 | //这里表示一行填充数据 47 | scans := make([]interface{}, len(columns)) 48 | //这里scans引用vals,把数据填充到[]byte里 49 | for k := range vals { 50 | scans[k] = &vals[k] 51 | } 52 | i := 0 53 | result := make(map[int]map[string]string) 54 | for rows.Next() { 55 | //填充数据 56 | rows.Scan(scans...) 57 | //每行数据 58 | row := make(map[string]string) 59 | //把vals中的数据复制到row中 60 | for k, v := range vals { 61 | key := columns[k] 62 | //这里把[]byte数据转成string 63 | row[key] = string(v) 64 | } 65 | //放入结果集 66 | result[i] = row 67 | i++ 68 | } 69 | return result 70 | } 71 | 72 | 73 | //根据结构体中sql标签映射数据到结构体中并且转换类型 74 | func DataToStructByTagSql(data map[string]string, obj interface{}) { 75 | objValue := reflect.ValueOf(obj).Elem() 76 | for i := 0; i < objValue.NumField(); i++ { 77 | 78 | if objValue.Field(i).Type().Kind()==reflect.Struct{ 79 | continue 80 | } 81 | 82 | value := data[objValue.Type().Field(i).Tag.Get("sql")] 83 | name := objValue.Type().Field(i).Name 84 | structFieldType := objValue.Field(i).Type() 85 | 86 | val := reflect.ValueOf(value) 87 | if !val.IsValid(){ 88 | continue 89 | } 90 | var err error 91 | if structFieldType != val.Type() { 92 | val, err = TypeConversion(value, structFieldType.Name()) 93 | if err != nil { 94 | 95 | } 96 | } 97 | objValue.FieldByName(name).Set(val) 98 | } 99 | } 100 | 101 | //类型转换 102 | func TypeConversion(value string, ntype string) (reflect.Value, error) { 103 | if ntype == "string" { 104 | return reflect.ValueOf(value), nil 105 | } else if ntype == "time.Time" { 106 | t, err := time.ParseInLocation("2006-01-02 15:04:05", value, time.Local) 107 | return reflect.ValueOf(t), err 108 | } else if ntype == "Time" { 109 | t, err := time.ParseInLocation("2006-01-02 15:04:05", value, time.Local) 110 | return reflect.ValueOf(t), err 111 | } else if ntype == "int" { 112 | i, err := strconv.Atoi(value) 113 | return reflect.ValueOf(i), err 114 | } else if ntype == "int8" { 115 | i, err := strconv.ParseInt(value, 10, 64) 116 | return reflect.ValueOf(int8(i)), err 117 | } else if ntype == "int32" { 118 | i, err := strconv.ParseInt(value, 10, 64) 119 | return reflect.ValueOf(int64(i)), err 120 | } else if ntype == "int64" { 121 | i, err := strconv.ParseInt(value, 10, 64) 122 | return reflect.ValueOf(i), err 123 | } else if ntype == "float32" { 124 | i, err := strconv.ParseFloat(value, 64) 125 | return reflect.ValueOf(float32(i)), err 126 | } else if ntype == "float64" { 127 | i, err := strconv.ParseFloat(value, 64) 128 | return reflect.ValueOf(i), err 129 | } 130 | 131 | //else if .......增加其他一些类型的转换 132 | 133 | return reflect.ValueOf(value), errors.New("未知的类型:" + ntype) 134 | } -------------------------------------------------------------------------------- /src/common/qetag.go: -------------------------------------------------------------------------------- 1 | package common 2 | 3 | import ( 4 | "bytes" 5 | "crypto/sha1" 6 | "encoding/base64" 7 | "io" 8 | "os" 9 | ) 10 | 11 | const ( 12 | BLOCK_BITS = 22 // Indicate that the blocksize is 4M 13 | BLOCK_SIZE = 1 << BLOCK_BITS 14 | ) 15 | 16 | func BlockCount(fsize int64) int { 17 | 18 | return int((fsize + (BLOCK_SIZE-1)) >> BLOCK_BITS) 19 | } 20 | 21 | func calSha1(b []byte, r io.Reader) ([]byte, error) { 22 | 23 | h := sha1.New() 24 | _, err := io.Copy(h, r) 25 | if err != nil { 26 | return nil, err 27 | } 28 | return h.Sum(b), nil 29 | } 30 | 31 | func GetEtag(filename string) (etag string, err error) { 32 | 33 | f, err := os.Open(filename) 34 | if err != nil { 35 | return 36 | } 37 | defer f.Close() 38 | 39 | fi, err := f.Stat() 40 | if err != nil { 41 | return 42 | } 43 | 44 | fsize := fi.Size() 45 | blockCnt := BlockCount(fsize) 46 | sha1Buf := make([]byte, 0, 21) 47 | 48 | if blockCnt <= 1 { // file size <= 4M 49 | sha1Buf = append(sha1Buf, 0x16) 50 | sha1Buf, err = calSha1(sha1Buf, f) 51 | if err != nil { 52 | return 53 | } 54 | } else { // file size > 4M 55 | sha1Buf = append(sha1Buf, 0x96) 56 | sha1BlockBuf := make([]byte, 0, blockCnt * 20) 57 | for i := 0; i < blockCnt; i ++ { 58 | body := io.LimitReader(f, BLOCK_SIZE) 59 | sha1BlockBuf, err = calSha1(sha1BlockBuf, body) 60 | if err != nil { 61 | return 62 | } 63 | } 64 | sha1Buf, _ = calSha1(sha1Buf, bytes.NewReader(sha1BlockBuf)) 65 | } 66 | etag = base64.URLEncoding.EncodeToString(sha1Buf) 67 | return 68 | } 69 | -------------------------------------------------------------------------------- /src/common/util.go: -------------------------------------------------------------------------------- 1 | package common 2 | 3 | import ( 4 | "crypto/sha1" 5 | "encoding/hex" 6 | ) 7 | 8 | 9 | 10 | // Sha1 return the data sha1 string 11 | func Sha1(data []byte) string { 12 | _sha1 := sha1.New() 13 | _sha1.Write(data) 14 | return hex.EncodeToString(_sha1.Sum([]byte(""))) 15 | } -------------------------------------------------------------------------------- /src/dao/IFileDao.go: -------------------------------------------------------------------------------- 1 | package dao 2 | 3 | import ( 4 | "database/sql" 5 | "fmt" 6 | "gocloud/common" 7 | "gocloud/datamodels" 8 | "time" 9 | ) 10 | 11 | type IFileDao interface { 12 | Conn() error 13 | //tbale_file 14 | SelectFile(fileqetag string) (file *datamodels.FileModel,err error) 15 | InsertFile(fileqetag string, filename string, filesize int64, fileaddr string) (succ bool,err error) 16 | //table_user_file 17 | SelectUserFiles(username string,parent_dir,status int64) (userfile []datamodels.UserFileModel, err error) 18 | SelectUserFilesByStatus(username string,status int64) (userfile []datamodels.UserFileModel, err error) 19 | SelectUserDirs(username string) (userfile []datamodels.UserFileModel, err error) 20 | SelectUserFilesByQetag(username,fileqetag string,parent_dir,status int64) (userfile *datamodels.UserFileModel, err error) 21 | SelectUserFilesByID(ID int64) (userfile *datamodels.UserFileModel, err error) 22 | InsertUserFile(username, fileqetag, filename string, filesize,is_dir,parent_dir int64) (succ bool,err error) 23 | DeleteUserFile()(succ bool,err error) 24 | UpdateUserFileStatus(status,id int64)(succ bool,err error) 25 | UpdateUserFileName(id int64,name string)(succ bool,err error) 26 | UpdateUserFileParentDir(id ,parent_dir int64)(succ bool,err error) 27 | //table_share_file 28 | InsertShareFile(user_file_id,share_time int64, pwd string) (id int64, err error) 29 | UpdateShareFileShareID(id int64, share_id string) (succ bool, err error) 30 | SelectShareFileBy(share_id string) (share *datamodels.FileShareModel, err error) 31 | SelectShareFileAndUserFile(share_id string) (share *datamodels.UserFileShareModel, err error) 32 | SelectUserShareFiles(user_name string) (share []datamodels.UserFileShareModel, err error) 33 | DeleteShareFileByID(share_id string) (succ bool, err error) 34 | SelectShareFileByUserFileId(user_file_id int64) (share *datamodels.FileShareModel, err error) 35 | } 36 | 37 | type fileDao struct { 38 | mysqlConn *sql.DB 39 | } 40 | 41 | func NewFileDao( conn *sql.DB) IFileDao { 42 | return &fileDao{mysqlConn: conn} 43 | } 44 | 45 | func (this *fileDao) Conn() error { 46 | if this.mysqlConn == nil { 47 | mysql, err := common.NewMysqlConn() 48 | if err != nil { 49 | return err 50 | } 51 | this.mysqlConn = mysql 52 | } 53 | 54 | return nil 55 | } 56 | 57 | func (this *fileDao) SelectFile(fileqetag string) (file *datamodels.FileModel,err error) { 58 | if err = this.Conn(); err != nil { 59 | return 60 | } 61 | stmt, err := this.mysqlConn.Prepare( 62 | "select file_qetag,file_addr,file_name,file_size,update_at from tbl_file " + 63 | "where file_qetag=? and status=1 limit 1") 64 | if err != nil { 65 | fmt.Println(err.Error()) 66 | return nil, err 67 | } 68 | defer stmt.Close() 69 | 70 | 71 | row, errRow := stmt.Query(fileqetag) 72 | 73 | if errRow != nil { 74 | return nil, errRow 75 | } 76 | 77 | result := common.GetResultRow(row) 78 | if len(result) == 0 { 79 | return nil, err 80 | } 81 | file = &datamodels.FileModel{} 82 | common.DataToStructByTagSql(result, file) 83 | 84 | return file,nil 85 | } 86 | 87 | // InsertUserFile : Add the file info to tbl_user_file 88 | func (this *fileDao) InsertUserFile(username, fileqetag, filename string, filesize,is_dir,parent_dir int64) (succ bool,err error) { 89 | if err = this.Conn(); err != nil { 90 | return 91 | } 92 | stmt, err := this.mysqlConn.Prepare( 93 | "insert ignore into tbl_user_file (`user_name`,`file_qetag`,`file_name`," + 94 | "`file_size`,`upload_at`,`is_dir`,`parent_dir`) values (?,?,?,?,?,?,?)") 95 | if err != nil { 96 | return false,err 97 | } 98 | defer stmt.Close() 99 | 100 | _, err = stmt.Exec(username, fileqetag, filename, filesize, time.Now(),is_dir,parent_dir) 101 | if err != nil { 102 | return false,err 103 | } 104 | return true,nil 105 | } 106 | 107 | // SelectUserFiles : Get the user first page files 108 | func (this *fileDao) SelectUserFiles(username string,parent_dir,status int64) (userfile []datamodels.UserFileModel, err error) { 109 | if err = this.Conn(); err != nil { 110 | return 111 | } 112 | stmt, err := this.mysqlConn.Prepare( 113 | "select id, file_qetag,file_name,file_size,upload_at," + 114 | "last_update,is_dir,parent_dir from tbl_user_file where user_name=? and parent_dir =? and status=?") 115 | if err != nil { 116 | return nil, err 117 | } 118 | defer stmt.Close() 119 | 120 | rows, err := stmt.Query(username,parent_dir,status) 121 | if err != nil { 122 | return nil, err 123 | } 124 | 125 | result := common.GetResultRows(rows) 126 | if len(result) == 0 { 127 | return nil, err 128 | } 129 | 130 | for _,v :=range result{ 131 | temp :=&datamodels.UserFileModel{} 132 | 133 | common.DataToStructByTagSql(v,temp) 134 | userfile = append(userfile, *temp) 135 | } 136 | 137 | return userfile, nil 138 | } 139 | 140 | func (this *fileDao) SelectUserFilesByStatus(username string,status int64) (userfile []datamodels.UserFileModel, err error) { 141 | if err = this.Conn(); err != nil { 142 | return 143 | } 144 | stmt, err := this.mysqlConn.Prepare( 145 | "select id, file_qetag,file_name,file_size,upload_at," + 146 | "last_update,is_dir,parent_dir from tbl_user_file where user_name=? and status=?") 147 | if err != nil { 148 | return nil, err 149 | } 150 | defer stmt.Close() 151 | 152 | rows, err := stmt.Query(username,status) 153 | if err != nil { 154 | return nil, err 155 | } 156 | 157 | result := common.GetResultRows(rows) 158 | if len(result) == 0 { 159 | return nil, err 160 | } 161 | 162 | for _,v :=range result{ 163 | temp :=&datamodels.UserFileModel{} 164 | 165 | common.DataToStructByTagSql(v,temp) 166 | userfile = append(userfile, *temp) 167 | } 168 | 169 | return userfile, nil 170 | } 171 | 172 | 173 | // InsertFile : 文件上传完成,保存meta 174 | func (this *fileDao) InsertFile(fileqetag string, filename string, filesize int64, fileaddr string) (succ bool,err error) { 175 | if err = this.Conn(); err != nil { 176 | return 177 | } 178 | stmt, err :=this.mysqlConn.Prepare("insert ignore into tbl_file" + 179 | "(`file_qetag`,`file_name`,`file_size`,`file_addr`,`status`) values(?,?,?,?,1)") 180 | if err != nil { 181 | fmt.Println("Failed to prepare statement,err:" + err.Error()) 182 | return false,err 183 | } 184 | defer stmt.Close() 185 | ret, err := stmt.Exec(fileqetag, filename, filesize, fileaddr) 186 | if err != nil { 187 | fmt.Println(err.Error()) 188 | return false,err 189 | } 190 | if rf, err := ret.RowsAffected(); nil == err { 191 | if rf <= 0 { 192 | fmt.Println("File with hash been upload before", fileqetag) 193 | } 194 | return true,err 195 | } 196 | return false,err 197 | } 198 | 199 | func (this *fileDao) DeleteUserFile()(succ bool,err error){ 200 | 201 | if err = this.Conn(); err != nil { 202 | return 203 | } 204 | stmt, err :=this.mysqlConn.Prepare("delete from tbl_user_file where id in " + 205 | " ( select id from( (select id from tbl_user_file where status =3 and TIMESTAMPDIFF(DAY, last_update, now())>30) )as sub)") 206 | if err != nil { 207 | fmt.Println("Failed to prepare statement,err:" + err.Error()) 208 | return false,err 209 | } 210 | defer stmt.Close() 211 | _, err = stmt.Exec() 212 | if err != nil { 213 | fmt.Println(err.Error()) 214 | return false,err 215 | } 216 | if nil != err { 217 | fmt.Println("delete file share has failed") 218 | return false,err 219 | 220 | } 221 | return true,nil 222 | } 223 | 224 | func (this *fileDao) SelectUserFilesByQetag(username,fileqetag string,parent_dir,status int64) (userfile *datamodels.UserFileModel, err error){ 225 | if err = this.Conn(); err != nil { 226 | return 227 | } 228 | stmt, err := this.mysqlConn.Prepare( 229 | "select id, file_qetag,file_name,file_size,upload_at," + 230 | "last_update,is_dir,parent_dir from tbl_user_file where user_name=? and parent_dir =? and status=? and file_qetag=?") 231 | if err != nil { 232 | return nil, err 233 | } 234 | defer stmt.Close() 235 | 236 | rows, err := stmt.Query(username,parent_dir,status,fileqetag) 237 | if err != nil { 238 | return nil, err 239 | } 240 | 241 | result := common.GetResultRow(rows) 242 | if len(result) == 0 { 243 | return nil, err 244 | } 245 | userfile =&datamodels.UserFileModel{} 246 | common.DataToStructByTagSql(result,userfile) 247 | 248 | return userfile, nil 249 | } 250 | 251 | 252 | func (this *fileDao) SelectUserFilesByID(ID int64) (userfile *datamodels.UserFileModel, err error){ 253 | if err = this.Conn(); err != nil { 254 | return 255 | } 256 | stmt, err := this.mysqlConn.Prepare( 257 | "select id, file_qetag,file_name,file_size,upload_at," + 258 | "last_update,is_dir,parent_dir from tbl_user_file where id=? ") 259 | if err != nil { 260 | return nil, err 261 | } 262 | defer stmt.Close() 263 | 264 | rows, err := stmt.Query(ID) 265 | if err != nil { 266 | return nil, err 267 | } 268 | 269 | result := common.GetResultRow(rows) 270 | if len(result) == 0 { 271 | return nil, err 272 | } 273 | userfile =&datamodels.UserFileModel{} 274 | common.DataToStructByTagSql(result,userfile) 275 | 276 | return userfile, nil 277 | } 278 | 279 | 280 | func (this *fileDao) UpdateUserFileStatus(status,id int64)(succ bool,err error){ 281 | if err = this.Conn(); err != nil { 282 | return 283 | } 284 | stmt, err :=this.mysqlConn.Prepare("update tbl_user_file set status = ?,last_update=? where" + 285 | " id=? ") 286 | if err != nil { 287 | fmt.Println("Failed to prepare statement,err:" + err.Error()) 288 | return false,err 289 | } 290 | defer stmt.Close() 291 | _, err = stmt.Exec(status,time.Now(), id) 292 | if err != nil { 293 | fmt.Println(err.Error()) 294 | return false,err 295 | } 296 | 297 | return true,nil 298 | } 299 | 300 | func (this *fileDao) UpdateUserFileName(id int64,name string)(succ bool,err error){ 301 | if err = this.Conn(); err != nil { 302 | return 303 | } 304 | stmt, err :=this.mysqlConn.Prepare("update tbl_user_file set file_name = ?,last_update=? where" + 305 | " id=? ") 306 | if err != nil { 307 | fmt.Println("Failed to prepare statement,err:" + err.Error()) 308 | return false,err 309 | } 310 | defer stmt.Close() 311 | _, err = stmt.Exec(name,time.Now(), id) 312 | if err != nil { 313 | fmt.Println(err.Error()) 314 | return false,err 315 | } 316 | 317 | return true,nil 318 | } 319 | 320 | func (this *fileDao) SelectUserDirs(username string) (userfile []datamodels.UserFileModel, err error){ 321 | if err = this.Conn(); err != nil { 322 | return 323 | } 324 | stmt, err := this.mysqlConn.Prepare( 325 | "select id, file_qetag,file_name,parent_dir from tbl_user_file where user_name=? and status=1 and is_dir=1") 326 | if err != nil { 327 | return nil, err 328 | } 329 | defer stmt.Close() 330 | rows, err := stmt.Query(username) 331 | if err != nil { 332 | return nil, err 333 | } 334 | 335 | result := common.GetResultRows(rows) 336 | if len(result) == 0 { 337 | return nil, err 338 | } 339 | 340 | for _,v :=range result{ 341 | temp :=&datamodels.UserFileModel{} 342 | 343 | common.DataToStructByTagSql(v,temp) 344 | userfile = append(userfile, *temp) 345 | } 346 | 347 | return userfile, nil 348 | } 349 | 350 | func (this *fileDao) UpdateUserFileParentDir(id ,parent_dir int64)(succ bool,err error){ 351 | if err = this.Conn(); err != nil { 352 | return 353 | } 354 | stmt, err :=this.mysqlConn.Prepare("update tbl_user_file set parent_dir = ?,last_update=? where" + 355 | " id=? ") 356 | if err != nil { 357 | fmt.Println("Failed to prepare statement,err:" + err.Error()) 358 | return false,err 359 | } 360 | defer stmt.Close() 361 | _, err = stmt.Exec(parent_dir,time.Now(), id) 362 | if err != nil { 363 | fmt.Println(err.Error()) 364 | return false,err 365 | } 366 | 367 | return true,nil 368 | } 369 | 370 | 371 | 372 | func (this *fileDao) InsertShareFile(user_file_id,share_time int64, pwd string) (id int64, err error) { 373 | if err = this.Conn(); err != nil { 374 | return 375 | } 376 | stmt, err :=this.mysqlConn.Prepare("insert ignore into tbl_user_share_file" + 377 | "(`user_file_id`,`share_time`,`create_at`,`share_pwd`) values(?,?,?,?)") 378 | if err != nil { 379 | fmt.Println("Failed to prepare statement,err:" + err.Error()) 380 | return -1,err 381 | } 382 | defer stmt.Close() 383 | ret, err := stmt.Exec(user_file_id,share_time, time.Now(), pwd) 384 | if err != nil { 385 | fmt.Println(err.Error()) 386 | return -1,err 387 | } 388 | if rf, err := ret.RowsAffected(); nil == err { 389 | if rf <= 0 { 390 | fmt.Println("Crate file share failed with :"+err.Error()) 391 | return -1,err 392 | } 393 | } 394 | 395 | return ret.LastInsertId() 396 | } 397 | 398 | 399 | 400 | func (this *fileDao) UpdateShareFileShareID(id int64, share_id string) (succ bool, err error) { 401 | if err = this.Conn(); err != nil { 402 | return 403 | } 404 | stmt, err :=this.mysqlConn.Prepare("update tbl_user_share_file set share_id =?" + 405 | " where id =?") 406 | if err != nil { 407 | fmt.Println("Failed to prepare statement,err:" + err.Error()) 408 | return false,err 409 | } 410 | defer stmt.Close() 411 | ret, err := stmt.Exec(share_id,id) 412 | if err != nil { 413 | fmt.Println(err.Error()) 414 | return false,err 415 | } 416 | if rf, err := ret.RowsAffected(); nil == err { 417 | if rf <= 0 { 418 | fmt.Println("Crate file share failed with :"+err.Error()) 419 | return false,err 420 | } 421 | 422 | } 423 | 424 | return true,nil 425 | } 426 | 427 | func (this *fileDao) SelectShareFileBy(share_id string) (share *datamodels.FileShareModel, err error) { 428 | if err = this.Conn(); err != nil { 429 | return 430 | } 431 | stmt, err :=this.mysqlConn.Prepare("select * from tbl_user_share_file" + 432 | " where share_id=?") 433 | if err != nil { 434 | fmt.Println("Failed to prepare statement,err:" + err.Error()) 435 | return nil,err 436 | } 437 | defer stmt.Close() 438 | row, err := stmt.Query(share_id) 439 | if err != nil { 440 | fmt.Println(err.Error()) 441 | return nil,err 442 | } 443 | 444 | 445 | result := common.GetResultRow(row) 446 | if len(result) == 0 { 447 | return nil, err 448 | } 449 | 450 | share =&datamodels.FileShareModel{} 451 | common.DataToStructByTagSql(result,share) 452 | return share,nil 453 | 454 | } 455 | 456 | func (this *fileDao) SelectShareFileAndUserFile(share_id string) (share *datamodels.UserFileShareModel, err error){ 457 | if err = this.Conn(); err != nil { 458 | return 459 | } 460 | stmt, err :=this.mysqlConn.Prepare( 461 | "select share_id,user_file_id,create_at,share_pwd,share_time, " + 462 | "tbl_user_file.id ,user_name,file_qetag,file_size,file_name,is_dir,parent_dir,upload_at,last_update,status " + 463 | "from tbl_user_share_file left join tbl_user_file on tbl_user_share_file.user_file_id = tbl_user_file.id " + 464 | " where share_id=?") 465 | if err != nil { 466 | fmt.Println("Failed to prepare statement,err:" + err.Error()) 467 | return nil,err 468 | } 469 | defer stmt.Close() 470 | row, err := stmt.Query(share_id) 471 | if err != nil { 472 | fmt.Println(err.Error()) 473 | return nil,err 474 | } 475 | 476 | 477 | result := common.GetResultRow(row) 478 | if len(result) == 0 { 479 | return nil, err 480 | } 481 | 482 | share =&datamodels.UserFileShareModel{} 483 | fileshare :=&datamodels.FileShareModel{} 484 | common.DataToStructByTagSql(result,fileshare) 485 | userfile :=&datamodels.UserFileModel{} 486 | common.DataToStructByTagSql(result,userfile) 487 | share.FileShareModel = *fileshare 488 | share.UserFileModel = *userfile 489 | 490 | 491 | return share,nil 492 | } 493 | 494 | func (this *fileDao) SelectUserShareFiles(user_name string) (share []datamodels.UserFileShareModel, err error){ 495 | if err = this.Conn(); err != nil { 496 | return 497 | } 498 | stmt, err :=this.mysqlConn.Prepare( 499 | " select tbl_user_file.file_name,tbl_user_file.is_dir,tbl_user_share_file.* from tbl_user_share_file " + 500 | " left join tbl_user_file on tbl_user_share_file.user_file_id=tbl_user_file.id " + 501 | " where tbl_user_file.user_name = ?") 502 | if err != nil { 503 | fmt.Println("Failed to prepare statement,err:" + err.Error()) 504 | return nil,err 505 | } 506 | defer stmt.Close() 507 | row, err := stmt.Query(user_name) 508 | if err != nil { 509 | fmt.Println(err.Error()) 510 | return nil,err 511 | } 512 | 513 | 514 | result := common.GetResultRows(row) 515 | if len(result) == 0 { 516 | return nil, err 517 | } 518 | 519 | share =[]datamodels.UserFileShareModel{} 520 | 521 | 522 | for _,v :=range result{ 523 | share_temp :=&datamodels.UserFileShareModel{} 524 | fileshare :=&datamodels.FileShareModel{} 525 | common.DataToStructByTagSql(v,fileshare) 526 | userfile :=&datamodels.UserFileModel{} 527 | common.DataToStructByTagSql(v,userfile) 528 | share_temp.FileShareModel = *fileshare 529 | share_temp.UserFileModel = *userfile 530 | share = append(share, *share_temp) 531 | } 532 | 533 | 534 | return share,nil 535 | } 536 | 537 | func (this *fileDao) DeleteShareFileByID(share_id string) (succ bool, err error) { 538 | if err = this.Conn(); err != nil { 539 | return 540 | } 541 | stmt, err :=this.mysqlConn.Prepare("delete from tbl_user_share_file " + 542 | " where share_id =?") 543 | if err != nil { 544 | fmt.Println("Failed to prepare statement,err:" + err.Error()) 545 | return false,err 546 | } 547 | defer stmt.Close() 548 | ret, err := stmt.Exec(share_id) 549 | if err != nil { 550 | fmt.Println(err.Error()) 551 | return false,err 552 | } 553 | if rf, err := ret.RowsAffected(); nil == err { 554 | if rf <= 0 { 555 | fmt.Println("delete file share has failed") 556 | return false,err 557 | } 558 | 559 | } 560 | 561 | return true,nil 562 | } 563 | 564 | func (this *fileDao) SelectShareFileByUserFileId(user_file_id int64) (share *datamodels.FileShareModel, err error) { 565 | if err = this.Conn(); err != nil { 566 | return 567 | } 568 | stmt, err :=this.mysqlConn.Prepare("select * from tbl_user_share_file" + 569 | " where user_file_id=?") 570 | if err != nil { 571 | fmt.Println("Failed to prepare statement,err:" + err.Error()) 572 | return nil,err 573 | } 574 | defer stmt.Close() 575 | row, err := stmt.Query(user_file_id) 576 | if err != nil { 577 | fmt.Println(err.Error()) 578 | return nil,err 579 | } 580 | 581 | 582 | result := common.GetResultRow(row) 583 | if len(result) == 0 { 584 | return nil, err 585 | } 586 | 587 | share =&datamodels.FileShareModel{} 588 | common.DataToStructByTagSql(result,share) 589 | return share,nil 590 | 591 | } 592 | -------------------------------------------------------------------------------- /src/dao/IUserDao.go: -------------------------------------------------------------------------------- 1 | package dao 2 | 3 | import ( 4 | "database/sql" 5 | "gocloud/common" 6 | "gocloud/datamodels" 7 | ) 8 | 9 | type IUserDao interface { 10 | Conn() error 11 | Insert(*datamodels.UserModel) (int64, error) 12 | SelectByEmail(email string) (user *datamodels.UserModel,err error) 13 | } 14 | 15 | 16 | type userDao struct { 17 | table string 18 | mysqlConn *sql.DB 19 | } 20 | 21 | func NewUserDao(table string, sql *sql.DB) IUserDao { 22 | return &userDao{table: table, mysqlConn: sql} 23 | } 24 | 25 | func (this *userDao) Conn() error { 26 | if this.mysqlConn == nil { 27 | mysql, err := common.NewMysqlConn() 28 | if err != nil { 29 | return err 30 | } 31 | this.mysqlConn = mysql 32 | } 33 | if this.table == "" { 34 | this.table = "tbl_user" 35 | } 36 | return nil 37 | } 38 | 39 | func (this *userDao) Insert(user *datamodels.UserModel) (ID int64, err error) { 40 | if err = this.Conn(); err != nil { 41 | return 42 | } 43 | 44 | sql := "insert into tbl_user (`user_name`,`user_pwd`,`email`) values (?,?,?)" 45 | 46 | stmt, errStmt := this.mysqlConn.Prepare(sql) 47 | defer stmt.Close() 48 | if errStmt != nil { 49 | return ID, errStmt 50 | } 51 | 52 | result, errResult := stmt.Exec(user.Username, user.Userpwd, user.Email) 53 | if errResult != nil { 54 | return ID, errResult 55 | } 56 | return result.LastInsertId() 57 | } 58 | 59 | func (this *userDao) SelectByEmail(email string) (user *datamodels.UserModel,err error){ 60 | if errConn := this.Conn(); errConn != nil { 61 | return &datamodels.UserModel{}, errConn 62 | } 63 | 64 | sql := "Select * From "+this.table+" where email= ?" 65 | stmt, errStmt := this.mysqlConn.Prepare(sql) 66 | defer stmt.Close() 67 | if errStmt != nil { 68 | return nil, errStmt 69 | } 70 | 71 | row, errRow := stmt.Query(email) 72 | 73 | if errRow != nil { 74 | return &datamodels.UserModel{}, errRow 75 | } 76 | 77 | result := common.GetResultRow(row) 78 | if len(result) == 0 { 79 | return &datamodels.UserModel{}, err 80 | } 81 | 82 | user = &datamodels.UserModel{} 83 | common.DataToStructByTagSql(result, user) 84 | return 85 | } 86 | 87 | 88 | 89 | -------------------------------------------------------------------------------- /src/datamodels/FileModel.go: -------------------------------------------------------------------------------- 1 | package datamodels 2 | 3 | type FileModel struct { 4 | FileQetag string `sql:"file_qetag"` 5 | FileName string`sql:"file_name"` 6 | FileSize int64`sql:"file_size"` 7 | Location string`sql:"file_addr"` 8 | UploadAt string`sql:"update_at"` 9 | } 10 | -------------------------------------------------------------------------------- /src/datamodels/FileShareModel.go: -------------------------------------------------------------------------------- 1 | package datamodels 2 | 3 | type FileShareModel struct { 4 | ID int `sql:"id"'` 5 | ShareId string `sql:"share_id"` 6 | UserFileId int`sql:"user_file_id"` 7 | CreateAt string `sql:"create_at"` 8 | SharePwd string `sql:"share_pwd"` 9 | ShareTime int `sql:"share_time"` 10 | } 11 | -------------------------------------------------------------------------------- /src/datamodels/RespModel.go: -------------------------------------------------------------------------------- 1 | package datamodels 2 | 3 | type RespModel struct { 4 | Status int //1 is succ 5 | Msg string 6 | Data interface{} 7 | } 8 | -------------------------------------------------------------------------------- /src/datamodels/UserFileModel.go: -------------------------------------------------------------------------------- 1 | package datamodels 2 | 3 | type UserFileModel struct { 4 | ID int `sql:"id"` 5 | UserName string `sql:"user_name"` 6 | FileQetag string `sql:"file_qetag"` 7 | FileName string `sql:"file_name"` 8 | FileSize int64 `sql:"file_size"` 9 | UploadAt string `sql:"upload_at"` 10 | LastUpdated string `sql:"last_update"` 11 | IsDir int64 `sql:"is_dir"` 12 | ParentDir int64 `sql:"parent_dir"` 13 | } 14 | -------------------------------------------------------------------------------- /src/datamodels/UserFileShareModel.go: -------------------------------------------------------------------------------- 1 | package datamodels 2 | 3 | type UserFileShareModel struct { 4 | FileShareModel 5 | UserFileModel 6 | } 7 | -------------------------------------------------------------------------------- /src/datamodels/UserModel.go: -------------------------------------------------------------------------------- 1 | package datamodels 2 | 3 | type UserModel struct { 4 | ID int `sql:"id"` 5 | Username string `sql:"user_name"` 6 | Userpwd string `sql:"user_pwd"` 7 | Email string `sql:"email"` 8 | Phone string `sql:"phone"` 9 | SignupAt string `sql:"signup_at"` 10 | LastActiveAt string `sql:"last_active"` 11 | Status int `sql:"status"` 12 | } 13 | -------------------------------------------------------------------------------- /src/go.mod: -------------------------------------------------------------------------------- 1 | module gocloud 2 | 3 | go 1.14 4 | 5 | require ( 6 | github.com/BurntSushi/toml v0.3.1 // indirect 7 | github.com/Joker/jade v1.0.0 // indirect 8 | github.com/Shopify/goreferrer v0.0.0-20181106222321-ec9c9a553398 // indirect 9 | github.com/eknkc/amber v0.0.0-20171010120322-cdade1c07385 // indirect 10 | github.com/fatih/structs v1.1.0 // indirect 11 | github.com/flosch/pongo2 v0.0.0-20190707114632-bbf5a6c351f4 // indirect 12 | github.com/go-sql-driver/mysql v1.5.0 13 | github.com/gorilla/schema v1.1.0 // indirect 14 | github.com/iris-contrib/blackfriday v2.0.0+incompatible // indirect 15 | github.com/iris-contrib/formBinder v5.0.0+incompatible // indirect 16 | github.com/iris-contrib/go.uuid v2.0.0+incompatible // indirect 17 | github.com/json-iterator/go v1.1.9 // indirect 18 | github.com/kataras/golog v0.0.13 // indirect 19 | github.com/kataras/iris/v12 v12.1.8 20 | github.com/klauspost/compress v1.10.5 // indirect 21 | github.com/microcosm-cc/bluemonday v1.0.2 // indirect 22 | github.com/ryanuber/columnize v2.1.0+incompatible // indirect 23 | github.com/shurcooL/sanitized_anchor_name v1.0.0 // indirect 24 | golang.org/x/crypto v0.0.0-20200429183012-4b2356b1ed79 // indirect 25 | gopkg.in/yaml.v2 v2.2.8 // indirect 26 | ) 27 | -------------------------------------------------------------------------------- /src/go.sum: -------------------------------------------------------------------------------- 1 | github.com/AndreasBriese/bbloom v0.0.0-20190306092124-e2d15f34fcf9/go.mod h1:bOvUY6CB00SOBii9/FifXqc0awNKxLFCL/+pkDPuyl8= 2 | github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= 3 | github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= 4 | github.com/CloudyKit/fastprinter v0.0.0-20200109182630-33d98a066a53 h1:sR+/8Yb4slttB4vD+b9btVEnWgL3Q00OBTzVT8B9C0c= 5 | github.com/CloudyKit/fastprinter v0.0.0-20200109182630-33d98a066a53/go.mod h1:+3IMCy2vIlbG1XG/0ggNQv0SvxCAIpPM5b1nCz56Xno= 6 | github.com/CloudyKit/jet/v3 v3.0.0 h1:1PwO5w5VCtlUUl+KTOBsTGZlhjWkcybsGaAau52tOy8= 7 | github.com/CloudyKit/jet/v3 v3.0.0/go.mod h1:HKQPgSJmdK8hdoAbKUUWajkHyHo4RaU5rMdUywE7VMo= 8 | github.com/Joker/hpp v0.0.0-20180418125244-6893e659854a/go.mod h1:MzD2WMdSxvbHw5fM/OXOFily/lipJWRc9C1px0Mt0ZE= 9 | github.com/Joker/hpp v1.0.0 h1:65+iuJYdRXv/XyN62C1uEmmOx3432rNG/rKlX6V7Kkc= 10 | github.com/Joker/hpp v1.0.0/go.mod h1:8x5n+M1Hp5hC0g8okX3sR3vFQwynaX/UgSOM9MeBKzY= 11 | github.com/Joker/jade v1.0.0 h1:lOCEPvTAtWfLpSZYMOv/g44MGQFAolbKh2khHHGu0Kc= 12 | github.com/Joker/jade v1.0.0/go.mod h1:efZIdO0py/LtcJRSa/j2WEklMSAw84WV0zZVMxNToB8= 13 | github.com/Shopify/goreferrer v0.0.0-20181106222321-ec9c9a553398 h1:WDC6ySpJzbxGWFh4aMxFFC28wwGp5pEuoTtvA4q/qQ4= 14 | github.com/Shopify/goreferrer v0.0.0-20181106222321-ec9c9a553398/go.mod h1:a1uqRtAwp2Xwc6WNPJEufxJ7fx3npB4UV/JOLmbu5I0= 15 | github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= 16 | github.com/aymerick/raymond v1.1.0 h1:phuNN2s67eI/HtO8CrvqFcdR2JP+BtkGJZ9n692Hr2Y= 17 | github.com/aymerick/raymond v2.0.2+incompatible h1:VEp3GpgdAnv9B2GFyTvqgcKvY+mfKMjPOA3SbKLtnU0= 18 | github.com/aymerick/raymond v2.0.2+incompatible/go.mod h1:osfaiScAUVup+UC9Nfq76eWqDhXlp+4UYaA8uhTBO6g= 19 | github.com/aymerick/raymond v2.0.3-0.20180322193309-b565731e1464+incompatible h1:Ppm0npCCsmuR9oQaBtRuZcmILVE74aXE+AmrJj8L2ns= 20 | github.com/aymerick/raymond v2.0.3-0.20180322193309-b565731e1464+incompatible/go.mod h1:osfaiScAUVup+UC9Nfq76eWqDhXlp+4UYaA8uhTBO6g= 21 | github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= 22 | github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk= 23 | github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= 24 | github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE= 25 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 26 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 27 | github.com/dgraph-io/badger v1.6.0/go.mod h1:zwt7syl517jmP8s94KqSxTlM6IMsdhYy6psNgSztDR4= 28 | github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= 29 | github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= 30 | github.com/eknkc/amber v0.0.0-20171010120322-cdade1c07385 h1:clC1lXBpe2kTj2VHdaIu9ajZQe4kcEY9j0NsnDDBZ3o= 31 | github.com/eknkc/amber v0.0.0-20171010120322-cdade1c07385/go.mod h1:0vRUJqYpeSZifjYj7uP3BG/gKcuzL9xWVV/Y+cK33KM= 32 | github.com/etcd-io/bbolt v1.3.3/go.mod h1:ZF2nL25h33cCyBtcyWeZ2/I3HQOfTP+0PIEvHjkjCrw= 33 | github.com/fatih/structs v1.1.0 h1:Q7juDM0QtcnhCpeyLGQKyg4TOIghuNXrkL32pHAUMxo= 34 | github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M= 35 | github.com/flosch/pongo2 v0.0.0-20190707114632-bbf5a6c351f4 h1:GY1+t5Dr9OKADM64SYnQjw/w99HMYvQ0A8/JoUkxVmc= 36 | github.com/flosch/pongo2 v0.0.0-20190707114632-bbf5a6c351f4/go.mod h1:T9YF2M40nIgbVgp3rreNmTged+9HrbNTIQf1PsaIiTA= 37 | github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= 38 | github.com/gavv/httpexpect v2.0.0+incompatible/go.mod h1:x+9tiU1YnrOvnB725RkpoLv1M62hOWzwo5OXotisrKc= 39 | github.com/go-check/check v0.0.0-20180628173108-788fd7840127/go.mod h1:9ES+weclKsC9YodN5RgxqK/VD9HM9JsCSh7rNhMZE98= 40 | github.com/go-sql-driver/mysql v1.5.0 h1:ozyZYNQW3x3HtqT1jira07DN2PArx2v7/mN66gGcHOs= 41 | github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= 42 | github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee h1:s+21KNqlpePfkah2I+gwHF8xmJWRjooY+5248k6m4A0= 43 | github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee/go.mod h1:L0fX3K22YWvt/FAX9NnzrNzcI4wNYi9Yku4O0LKYflo= 44 | github.com/gobwas/pool v0.2.0 h1:QEmUOlnSjWtnpRGHF3SauEiOsy82Cup83Vf2LcMlnc8= 45 | github.com/gobwas/pool v0.2.0/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw= 46 | github.com/gobwas/ws v1.0.2 h1:CoAavW/wd/kulfZmSIBt6p24n4j7tHgNVCjsfHVNUbo= 47 | github.com/gobwas/ws v1.0.2/go.mod h1:szmBTxLgaFppYjEmNtny/v3w89xOydFnnZMcgRRu/EM= 48 | github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 49 | github.com/gomodule/redigo v1.7.1-0.20190724094224-574c33c3df38/go.mod h1:B4C85qUVwatsJoIUNIfCRsp7qO0iAmpGFZ4EELWSbC4= 50 | github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= 51 | github.com/gorilla/schema v1.1.0 h1:CamqUDOFUBqzrvxuz2vEwo8+SUdwsluFh7IlzJh30LY= 52 | github.com/gorilla/schema v1.1.0/go.mod h1:kgLaKoK1FELgZqMAVxx/5cbj0kT+57qxUrAlIO2eleU= 53 | github.com/gorilla/websocket v1.4.1 h1:q7AeDBpnBk8AogcD4DSag/Ukw/KV+YhzLj2bP5HvKCM= 54 | github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= 55 | github.com/hashicorp/go-version v1.2.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= 56 | github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= 57 | github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= 58 | github.com/iris-contrib/blackfriday v2.0.0+incompatible h1:o5sHQHHm0ToHUlAJSTjW9UWicjJSDDauOOQ2AHuIVp4= 59 | github.com/iris-contrib/blackfriday v2.0.0+incompatible/go.mod h1:UzZ2bDEoaSGPbkg6SAB4att1aAwTmVIx/5gCVqeyUdI= 60 | github.com/iris-contrib/formBinder v5.0.0+incompatible h1:jL+H+cCSEV8yzLwVbBI+tLRN/PpVatZtUZGK9ldi3bU= 61 | github.com/iris-contrib/formBinder v5.0.0+incompatible/go.mod h1:i8kTYUOEstd/S8TG0ChTXQdf4ermA/e8vJX0+QruD9w= 62 | github.com/iris-contrib/go.uuid v2.0.0+incompatible h1:XZubAYg61/JwnJNbZilGjf3b3pB80+OQg2qf6c8BfWE= 63 | github.com/iris-contrib/go.uuid v2.0.0+incompatible/go.mod h1:iz2lgM/1UnEf1kP0L/+fafWORmlnuysV2EMP8MW+qe0= 64 | github.com/iris-contrib/jade v1.1.3 h1:p7J/50I0cjo0wq/VWVCDFd8taPJbuFC+bq23SniRFX0= 65 | github.com/iris-contrib/jade v1.1.3/go.mod h1:H/geBymxJhShH5kecoiOCSssPX7QWYH7UaeZTSWddIk= 66 | github.com/iris-contrib/pongo2 v0.0.1 h1:zGP7pW51oi5eQZMIlGA3I+FHY9/HOQWDB+572yin0to= 67 | github.com/iris-contrib/pongo2 v0.0.1/go.mod h1:Ssh+00+3GAZqSQb30AvBRNxBx7rf0GqwkjqxNd0u65g= 68 | github.com/iris-contrib/schema v0.0.1 h1:10g/WnoRR+U+XXHWKBHeNy/+tZmM2kcAVGLOsz+yaDA= 69 | github.com/iris-contrib/schema v0.0.1/go.mod h1:urYA3uvUNG1TIIjOSCzHr9/LmbQo8LrOcOqfqxa4hXw= 70 | github.com/json-iterator/go v1.1.9 h1:9yzud/Ht36ygwatGx56VwCZtlI/2AD15T1X2sjSuGns= 71 | github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= 72 | github.com/juju/errors v0.0.0-20181118221551-089d3ea4e4d5 h1:rhqTjzJlm7EbkELJDKMTU7udov+Se0xZkWmugr6zGok= 73 | github.com/juju/errors v0.0.0-20181118221551-089d3ea4e4d5/go.mod h1:W54LbzXuIE0boCoNJfwqpmkKJ1O4TCTZMetAt6jGk7Q= 74 | github.com/juju/loggo v0.0.0-20180524022052-584905176618/go.mod h1:vgyd7OREkbtVEN/8IXZe5Ooef3LQePvuBm9UWj6ZL8U= 75 | github.com/juju/testing v0.0.0-20180920084828-472a3e8b2073/go.mod h1:63prj8cnj0tU0S9OHjGJn+b1h0ZghCndfnbQolrYTwA= 76 | github.com/kataras/golog v0.0.10/go.mod h1:yJ8YKCmyL+nWjERB90Qwn+bdyBZsaQwU3bTVFgkFIp8= 77 | github.com/kataras/golog v0.0.13 h1:zWzk56eWBkdV4kPq3Koh8PS4pUfNQUfHuLc3YACpM7c= 78 | github.com/kataras/golog v0.0.13/go.mod h1:lMz0gaBYitlMKvVn2Ykw0WpRoqHrq1kjWId/iLrH67I= 79 | github.com/kataras/iris v11.1.1+incompatible h1:c2iRKvKLpTYMXKdVB8YP/+A67NtZFt9kFFy+ZwBhWD0= 80 | github.com/kataras/iris v11.1.1+incompatible/go.mod h1:ki9XPua5SyAJbIxDdsssxevgGrbpBmmvoQmo/A0IodY= 81 | github.com/kataras/iris/v12 v12.1.8 h1:O3gJasjm7ZxpxwTH8tApZsvf274scSGQAUpNe47c37U= 82 | github.com/kataras/iris/v12 v12.1.8/go.mod h1:LMYy4VlP67TQ3Zgriz8RE2h2kMZV2SgMYbq3UhfoFmE= 83 | github.com/kataras/neffos v0.0.14 h1:pdJaTvUG3NQfeMbbVCI8JT2T5goPldyyfUB2PJfh1Bs= 84 | github.com/kataras/neffos v0.0.14/go.mod h1:8lqADm8PnbeFfL7CLXh1WHw53dG27MC3pgi2R1rmoTE= 85 | github.com/kataras/pio v0.0.2/go.mod h1:hAoW0t9UmXi4R5Oyq5Z4irTbaTsOemSrDGUtaTl7Dro= 86 | github.com/kataras/pio v0.0.6 h1:Fk21upgXuB6VKHuH/sY13g6fzA5Gn+rKjV0ReXST484= 87 | github.com/kataras/pio v0.0.6/go.mod h1:NFfMp2kVP1rmV4N6gH6qgWpuoDKlrOeYi3VrAIWCGsE= 88 | github.com/kataras/sitemap v0.0.5 h1:4HCONX5RLgVy6G4RkYOV3vKNcma9p236LdGOipJsaFE= 89 | github.com/kataras/sitemap v0.0.5/go.mod h1:KY2eugMKiPwsJgx7+U103YZehfvNGOXURubcGyk0Bz8= 90 | github.com/klauspost/compress v1.9.7/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= 91 | github.com/klauspost/compress v1.10.5 h1:7q6vHIqubShURwQz8cQK6yIe/xC3IF0Vm7TGfqjewrc= 92 | github.com/klauspost/compress v1.10.5/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= 93 | github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= 94 | github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= 95 | github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= 96 | github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= 97 | github.com/mattn/goveralls v0.0.2/go.mod h1:8d1ZMHsd7fW6IRPKQh46F2WRpyib5/X4FOpevwGNQEw= 98 | github.com/mediocregopher/radix/v3 v3.4.2 h1:galbPBjIwmyREgwGCfQEN4X8lxbJnKBYurgz+VfcStA= 99 | github.com/mediocregopher/radix/v3 v3.4.2/go.mod h1:8FL3F6UQRXHXIBSPUs5h0RybMF8i4n7wVopoX3x7Bv8= 100 | github.com/microcosm-cc/bluemonday v1.0.2 h1:5lPfLTTAvAbtS0VqT+94yOtFnGfUWYyx0+iToC3Os3s= 101 | github.com/microcosm-cc/bluemonday v1.0.2/go.mod h1:iVP4YcDBq+n/5fb23BhYFvIMq/leAFZyRl6bYmGDlGc= 102 | github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= 103 | github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= 104 | github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 h1:ZqeYNhU3OHLH3mGKHDcjJRFFRrJa6eAM5H+CtDdOsPc= 105 | github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= 106 | github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742 h1:Esafd1046DLDQ0W1YjYsBW+p8U2u7vzgW2SQVmlNazg= 107 | github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= 108 | github.com/nats-io/jwt v0.3.0 h1:xdnzwFETV++jNc4W1mw//qFyJGb2ABOombmZJQS4+Qo= 109 | github.com/nats-io/jwt v0.3.0/go.mod h1:fRYCDE99xlTsqUzISS1Bi75UBJ6ljOJQOAAu5VglpSg= 110 | github.com/nats-io/nats.go v1.9.1 h1:ik3HbLhZ0YABLto7iX80pZLPw/6dx3T+++MZJwLnMrQ= 111 | github.com/nats-io/nats.go v1.9.1/go.mod h1:ZjDU1L/7fJ09jvUSRVBR2e7+RnLiiIQyqyzEE/Zbp4w= 112 | github.com/nats-io/nkeys v0.1.0 h1:qMd4+pRHgdr1nAClu+2h/2a5F2TmKcCzjCDazVgRoX4= 113 | github.com/nats-io/nkeys v0.1.0/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w= 114 | github.com/nats-io/nuid v1.0.1 h1:5iA8DT8V7q8WK2EScv2padNa/rTESc1KdnPw4TC2paw= 115 | github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c= 116 | github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= 117 | github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 118 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 119 | github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= 120 | github.com/ryanuber/columnize v1.1.1 h1:kaLR0w/IEQSUuivlqIGTq3RXnF7Xi5PfA2ekiHVsvQc= 121 | github.com/ryanuber/columnize v2.1.0+incompatible h1:j1Wcmh8OrK4Q7GXY+V7SVSY8nUWQxHW5TkBe7YUl+2s= 122 | github.com/ryanuber/columnize v2.1.0+incompatible/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= 123 | github.com/schollz/closestmatch v2.1.0+incompatible h1:Uel2GXEpJqOWBrlyI+oY9LTiyyjYS17cCYRqP13/SHk= 124 | github.com/schollz/closestmatch v2.1.0+incompatible/go.mod h1:RtP1ddjLong6gTkbtmuhtR2uUrrJOpYzYRvbcPAid+g= 125 | github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo= 126 | github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= 127 | github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= 128 | github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= 129 | github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU= 130 | github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= 131 | github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= 132 | github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s= 133 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 134 | github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= 135 | github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= 136 | github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= 137 | github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= 138 | github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= 139 | golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= 140 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 141 | golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 142 | golang.org/x/crypto v0.0.0-20191227163750-53104e6ec876/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= 143 | golang.org/x/crypto v0.0.0-20200429183012-4b2356b1ed79 h1:IaQbIIB2X/Mp/DKctl6ROxz1KyMlKp4uyvL6+kQ7C88= 144 | golang.org/x/crypto v0.0.0-20200429183012-4b2356b1ed79/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= 145 | golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 146 | golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 147 | golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 148 | golang.org/x/net v0.0.0-20190327091125-710a502c58a2/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 149 | golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3 h1:0GoQqolDA55aaLxZyTzK/Y2ePZzZTUrRacwib7cNsYQ= 150 | golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 151 | golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 152 | golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553 h1:efeOvDhwQ29Dj3SdAV/MJf8oukgn+8D8WgaCaRMchF8= 153 | golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 154 | golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 155 | golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 156 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 157 | golang.org/x/sys v0.0.0-20190412213103-97732733099d h1:+R4KGOnez64A81RvjARKc4UT5/tI9ujCIVX+P5KiHuI= 158 | golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 159 | golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb h1:fgwFCsaw9buMuxNd6+DQfAuSFqbNiQZpcgJQAgJsK6k= 160 | golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 161 | golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg= 162 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 163 | golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs= 164 | golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= 165 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 166 | golang.org/x/tools v0.0.0-20181221001348-537d06c36207/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 167 | golang.org/x/tools v0.0.0-20190327201419-c70d86f8b7cf/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 168 | golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898 h1:/atklqdjdhuosWIl6AIbOeHJjicWYPqR9bpxqxYG2pA= 169 | golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 170 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 171 | gopkg.in/ini.v1 v1.51.1 h1:GyboHr4UqMiLUybYjd22ZjQIKEJEpgtLXtuGbR21Oho= 172 | gopkg.in/ini.v1 v1.51.1/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= 173 | gopkg.in/mgo.v2 v2.0.0-20180705113604-9856a29383ce/go.mod h1:yeKp02qBN3iKW1OzL3MGk2IdtZzaj7SFntXj72NppTA= 174 | gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 175 | gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10= 176 | gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 177 | gopkg.in/yaml.v3 v3.0.0-20191120175047-4206685974f2 h1:XZx7nhd5GMaZpmDaEHFVafUZC7ya0fuo7cSJ3UCKYmM= 178 | gopkg.in/yaml.v3 v3.0.0-20191120175047-4206685974f2/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 179 | -------------------------------------------------------------------------------- /src/internal/app.go: -------------------------------------------------------------------------------- 1 | package internal 2 | 3 | import ( 4 | "database/sql" 5 | "github.com/kataras/iris/v12" 6 | "github.com/kataras/iris/v12/mvc" 7 | "github.com/kataras/iris/v12/sessions" 8 | "gocloud/common" 9 | "gocloud/dao" 10 | "gocloud/services" 11 | "gocloud/web/controllers" 12 | "gocloud/web/middleware" 13 | "time" 14 | ) 15 | 16 | func AppRun() { 17 | //1. create iris instance 18 | app := iris.New() 19 | app.Favicon("./web/assets/img/fav.ico") 20 | //2. set logger level 21 | app.Logger().SetLevel("debug") 22 | //3. register view 23 | tmplate := iris.HTML("./web/views", ".html").Layout("shared/layout.html").Reload(true) 24 | tmplate_framework := iris.HTML("./web/views", ".html").Layout("shared/layout.fw.html").Reload(true) 25 | app.RegisterView(tmplate) 26 | app.RegisterView(tmplate_framework) 27 | 28 | app.HandleDir("/assets", "./web/assets") 29 | 30 | 31 | app.OnAnyErrorCode(func(ctx iris.Context) { 32 | ctx.ViewLayout("shared/layout.fw.html") 33 | ctx.View("error/error.html") 34 | }) 35 | 36 | sess := sessions.New(sessions.Config{ 37 | // Cookie string, the session's client cookie name, for example: "_session_id" 38 | // 39 | // Defaults to "irissessionid" 40 | Cookie: "_session_id", 41 | // it's time.Duration, from the time cookie is created, how long it can be alive? 42 | // 0 means no expire, unlimited life. 43 | // -1 means expire when browser closes 44 | // or set a value, like 2 hours: 45 | Expires: time.Hour * 2, 46 | // if you want to invalid cookies on different subdomains 47 | // of the same host, then enable it. 48 | // Defaults to false. 49 | DisableSubdomainPersistence: false, 50 | // Allow getting the session value stored by the request from the same request. 51 | AllowReclaim: true, 52 | }) 53 | 54 | app.Use(sess.Handler()) 55 | 56 | db := getDb() 57 | registerHandler(app,db) 58 | 59 | // file recycle for last_update > 30 days 60 | go startFileRecycle() 61 | 62 | // run web app 63 | app.Listen(":8080",iris.WithOptimizations) 64 | 65 | 66 | } 67 | 68 | func getDb() *sql.DB{ 69 | //连接数据库 70 | db, err := common.NewMysqlConn() 71 | if err != nil { 72 | panic(err) 73 | } 74 | return db 75 | } 76 | 77 | func registerHandler(app *iris.Application,db *sql.DB) { 78 | 79 | //Index 80 | indexService := services.NewIndexService() 81 | indexParty := app.Party("/") 82 | indexParty.Use(middleware.NewAuth()) 83 | index := mvc.New(indexParty) 84 | index.Register(indexService) 85 | index.Handle(new(controllers.IndexController)) 86 | 87 | //Login 88 | loginDao :=dao.NewUserDao("tbl_user",db) 89 | loginService := services.NewLoginService(loginDao) 90 | loginParty := app.Party("/login") 91 | login := mvc.New(loginParty) 92 | login.Register(loginService) 93 | login.Handle(new(controllers.LoginController)) 94 | 95 | //File 96 | fileDao :=dao.NewFileDao(db) 97 | fileService :=services.NewFileService(fileDao) 98 | file:=mvc.New(app.Party("/file",middleware.NewAuth())) 99 | file.Register(fileService) 100 | file.Handle(new(controllers.FileController)) 101 | 102 | //share 103 | share:=mvc.New(app.Party("/share")) 104 | share.Register(fileService) 105 | share.Handle(new(controllers.ShareController)) 106 | 107 | 108 | } 109 | 110 | func startFileRecycle() { 111 | db := getDb() 112 | fileDao :=dao.NewFileDao(db) 113 | fileService :=services.NewFileService(fileDao) 114 | 115 | for { 116 | fileService.DeleteRecyle() 117 | time.Sleep(time.Minute) 118 | } 119 | 120 | } 121 | -------------------------------------------------------------------------------- /src/services/FileService_test.go: -------------------------------------------------------------------------------- 1 | package services 2 | 3 | import ( 4 | "gocloud/datamodels" 5 | "testing" 6 | ) 7 | 8 | func TestCreateTree(t *testing.T) { 9 | user_dir := []datamodels.UserFileModel{ 10 | {FileName: "1",IsDir: 1,ID: 1,ParentDir: 0}, 11 | {FileName: "2",IsDir: 1,ID: 2,ParentDir: 0}, 12 | {FileName: "3",IsDir: 1,ID: 3,ParentDir: 0}, 13 | {FileName: "11",IsDir: 1,ID: 11,ParentDir: 1}, 14 | {FileName: "111",IsDir: 1,ID: 111,ParentDir: 11}, 15 | {FileName: "22",IsDir: 1,ID: 22,ParentDir: 2}, 16 | } 17 | dirs := &[]map[string]interface{}{} 18 | root := getNode(user_dir,0) 19 | createTree(dirs,user_dir,root,100) 20 | } 21 | -------------------------------------------------------------------------------- /src/services/IFileService.go: -------------------------------------------------------------------------------- 1 | package services 2 | 3 | import ( 4 | "errors" 5 | "gocloud/dao" 6 | "gocloud/datamodels" 7 | "math/big" 8 | ) 9 | 10 | type IFileService interface { 11 | GetFileMeta(fileqetag string) (file *datamodels.FileModel,err error) 12 | AddFile(fileqetag string, filename string, filesize int64, fileaddr string) (succ bool,err error) 13 | QueryUserFils(username string,parent_dir ,status int64) (userfile []datamodels.UserFileModel, err error) 14 | QueryUserFilsByStatus(username string,status int64) (userfile []datamodels.UserFileModel, err error) 15 | GetUserFileByID(id int64) (userfile *datamodels.UserFileModel, err error) 16 | AddUserFileRelation(username, fileqetag, filename,fileaddr string, filesize ,is_dir,parent_dir int64) (succ bool,err error) 17 | DeleteFile(username, fileqetag string,parent_id int64)(succ bool,err error) 18 | UpdateUserFileName(id int64,name string)(succ bool,err error) 19 | GetUserDirByUser(user_name string,ignoreNode int)(dirs *[]map[string]interface{},err error) 20 | MoveFileTo(id, parent_dir int64) ( bool, error) 21 | DeleteRecyle() ( bool, error) 22 | 23 | 24 | CreateShareFile(user_file_id,share_id int64, pwd string) (share_link string,succ bool, err error) 25 | QueryShareFileBy(qetag string) (share *datamodels.FileShareModel, err error) 26 | QueryShareFileAndValid(share_id,pwd string) (share *datamodels.FileShareModel, err error) 27 | QueryUserShareFileBy(share_id string) (share *datamodels.UserFileShareModel, err error) 28 | QueryUserShareFiles(user_name string) (share []datamodels.UserFileShareModel, err error) 29 | CancelShareFile(share_id string) (succ bool, err error) 30 | QueryShareFileByUserFileId(user_file_id int64) (share *datamodels.FileShareModel, err error) 31 | } 32 | 33 | type fileService struct { 34 | dao dao.IFileDao 35 | } 36 | 37 | func NewFileService(dao dao.IFileDao) IFileService { 38 | return &fileService{dao} 39 | } 40 | 41 | func (this *fileService) GetFileMeta(fileqetag string) (file *datamodels.FileModel,err error){ 42 | return this.dao.SelectFile(fileqetag) 43 | } 44 | func (this *fileService) AddFile(fileqetag string, filename string, filesize int64, fileaddr string) (succ bool,err error){ 45 | return this.dao.InsertFile(fileqetag,filename,filesize,fileaddr) 46 | } 47 | func (this *fileService) QueryUserFils(username string,parent_dir ,status int64) (userfile []datamodels.UserFileModel, err error) { 48 | return this.dao.SelectUserFiles(username,parent_dir,status) 49 | } 50 | func (this *fileService) QueryUserFilsByStatus(username string,status int64) (userfile []datamodels.UserFileModel, err error) { 51 | return this.dao.SelectUserFilesByStatus(username,status) 52 | } 53 | 54 | func (this *fileService) GetUserFileByID(id int64) (userfile *datamodels.UserFileModel, err error){ 55 | return this.dao.SelectUserFilesByID(id) 56 | } 57 | 58 | func (this *fileService) AddUserFileRelation(username, fileqetag, filename,fileaddr string, filesize,is_dir,parent_dir int64) (succ bool,err error){ 59 | 60 | // if the file was deleted,we only need to do confirm and update the file status 61 | file ,err :=this.dao.SelectUserFilesByQetag(username,fileqetag,parent_dir,3) 62 | if err!=nil{ 63 | return false,nil 64 | } 65 | if file!=nil { 66 | return this.dao.UpdateUserFileStatus(1,int64(file.ID)) 67 | } 68 | // else we insert a new record 69 | return this.dao.InsertUserFile(username, fileqetag, filename ,filesize,is_dir,parent_dir) 70 | } 71 | 72 | func (this *fileService) DeleteFile(username, fileqetag string,parent_id int64)(succ bool,err error){ 73 | 74 | // if is a directory ,need it is empty 75 | file,err :=this.dao.SelectUserFiles(username,parent_id,1) 76 | if err!=nil{ 77 | return 78 | } 79 | if len(file)>0{ 80 | err=errors.New("the dir not empty!") 81 | return 82 | } 83 | // soft delete by update status 84 | return this.dao.UpdateUserFileStatus(3,parent_id) 85 | } 86 | 87 | func (this *fileService) UpdateUserFileName(id int64,name string)(succ bool,err error){ 88 | return this.dao.UpdateUserFileName(id,name) 89 | } 90 | 91 | func (this *fileService) GetUserDirByUser(user_name string,ignoreNode int)(dirs *[]map[string]interface{},err error){ 92 | user_dir,err:= this.dao.SelectUserDirs(user_name) 93 | if err!=nil{ 94 | return nil,err 95 | } 96 | dirs = &[]map[string]interface{}{} 97 | root := getNode(user_dir,0) 98 | createTree(dirs,user_dir,root,ignoreNode) 99 | return dirs,nil 100 | } 101 | 102 | func (this *fileService) MoveFileTo(id, parent_dir int64) ( bool, error) { 103 | return this.dao.UpdateUserFileParentDir(id,parent_dir) 104 | } 105 | 106 | func (this *fileService) DeleteRecyle() ( bool, error) { 107 | return this.dao.DeleteUserFile() 108 | } 109 | 110 | 111 | func (this *fileService) CreateShareFile(user_file_id ,share_time int64, pwd string) (share_link string,succ bool, err error){ 112 | id,err := this.dao.InsertShareFile(user_file_id,share_time,pwd) 113 | if err!=nil{ 114 | return "",false,err 115 | } 116 | share_link =big.NewInt(id).Text(62) 117 | succ,err = this.dao.UpdateShareFileShareID(id,share_link) 118 | if !succ||err!=nil{ 119 | return "",false,err 120 | } 121 | return 122 | 123 | } 124 | 125 | func (this *fileService) QueryShareFileBy(share_id string) (share *datamodels.FileShareModel, err error){ 126 | return this.dao.SelectShareFileBy(share_id) 127 | } 128 | 129 | func (this *fileService) QueryUserShareFileBy(share_id string) (share *datamodels.UserFileShareModel, err error){ 130 | return this.dao.SelectShareFileAndUserFile(share_id) 131 | } 132 | 133 | func (this *fileService) QueryShareFileAndValid(share_id,pwd string) (share *datamodels.FileShareModel, err error){ 134 | share ,err = this.dao.SelectShareFileBy(share_id) 135 | 136 | if err!=nil{ 137 | return nil, err 138 | } 139 | 140 | if pwd !=share.SharePwd{ 141 | return nil,errors.New("the share password invalid") 142 | } 143 | 144 | 145 | return share,nil 146 | } 147 | 148 | func (this *fileService) QueryUserShareFiles(user_name string) (share []datamodels.UserFileShareModel, err error){ 149 | 150 | return this.dao.SelectUserShareFiles(user_name) 151 | } 152 | 153 | func (this *fileService) CancelShareFile(share_id string) (succ bool, err error){ 154 | 155 | return this.dao.DeleteShareFileByID(share_id) 156 | } 157 | 158 | func (this *fileService) QueryShareFileByUserFileId(user_file_id int64) (share *datamodels.FileShareModel, err error){ 159 | 160 | return this.dao.SelectShareFileByUserFileId(user_file_id) 161 | } 162 | 163 | 164 | 165 | func createTree(tree *[]map[string]interface{},dirs []datamodels.UserFileModel,nodes []datamodels.UserFileModel,ignoreNode int) { 166 | 167 | for _,v :=range nodes{ 168 | if v.ID == ignoreNode { 169 | continue 170 | } 171 | _node :=map[string]interface{}{} 172 | _node["id"] = v.ID 173 | _node["label"] = v.FileName 174 | *tree = append(*tree, _node) 175 | 176 | temp := getNode(dirs,int64(v.ID)) 177 | if len(temp)>0{ 178 | children :=&[]map[string]interface{}{} 179 | _node["children"] = children 180 | createTree(children,dirs,temp,ignoreNode) 181 | } 182 | } 183 | 184 | } 185 | 186 | func getNode(data []datamodels.UserFileModel,parent_id int64) []datamodels.UserFileModel { 187 | temp :=[]datamodels.UserFileModel{} 188 | for _,v :=range data{ 189 | if v.ParentDir == parent_id{ 190 | temp = append(temp, v) 191 | } 192 | } 193 | return temp 194 | } 195 | 196 | 197 | -------------------------------------------------------------------------------- /src/services/IIndexService.go: -------------------------------------------------------------------------------- 1 | package services 2 | 3 | type IIndexService interface { 4 | GetIndex()(string,error) 5 | } 6 | 7 | type IndexService struct { 8 | 9 | } 10 | 11 | func NewIndexService() *IndexService { 12 | return &IndexService{} 13 | } 14 | 15 | func (this *IndexService) GetIndex() (string,error) { 16 | 17 | return "",nil 18 | } 19 | 20 | -------------------------------------------------------------------------------- /src/services/ILoginService.go: -------------------------------------------------------------------------------- 1 | package services 2 | 3 | import ( 4 | "gocloud/common" 5 | "gocloud/dao" 6 | "gocloud/datamodels" 7 | ) 8 | 9 | type ILoginService interface { 10 | Signup(*datamodels.UserModel)(int64,error) 11 | Signin(*datamodels.UserModel)(*datamodels.UserModel,error) 12 | } 13 | 14 | type LoginService struct { 15 | dao dao.IUserDao 16 | } 17 | 18 | func NewLoginService(d dao.IUserDao) ILoginService { 19 | return &LoginService{d} 20 | } 21 | 22 | func (this *LoginService) Signup(user *datamodels.UserModel) (int64, error) { 23 | user.Userpwd = common.Sha1([]byte(user.Userpwd+common.User_Pwd_Sha1_Salt)) 24 | return this.dao.Insert(user) 25 | } 26 | 27 | func (this *LoginService) Signin(user *datamodels.UserModel) (*datamodels.UserModel, error) { 28 | return this.dao.SelectByEmail(user.Email) 29 | } 30 | -------------------------------------------------------------------------------- /src/web/assets/css/fonts/element-icons.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Code-Fight/GoCloud/c4a794c588e17a9ca66715c284cd5ec1161d2d9f/src/web/assets/css/fonts/element-icons.ttf -------------------------------------------------------------------------------- /src/web/assets/css/fonts/element-icons.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Code-Fight/GoCloud/c4a794c588e17a9ca66715c284cd5ec1161d2d9f/src/web/assets/css/fonts/element-icons.woff -------------------------------------------------------------------------------- /src/web/assets/css/login.css: -------------------------------------------------------------------------------- 1 | * { 2 | box-sizing: border-box; 3 | } 4 | 5 | body { 6 | font-family: 'Montserrat', sans-serif; 7 | background: #f6f5f7; 8 | display: flex; 9 | flex-direction: column; 10 | justify-content: center; 11 | align-items: center; 12 | height: 100vh; 13 | margin: -20px 0 50px; 14 | } 15 | 16 | h1 { 17 | font-weight: bold; 18 | margin: 0; 19 | } 20 | 21 | p { 22 | font-size: 14px; 23 | line-height: 20px; 24 | letter-spacing: .5px; 25 | margin: 20px 0 30px; 26 | } 27 | 28 | span { 29 | font-size: 12px; 30 | } 31 | 32 | a { 33 | color: #333; 34 | font-size: 14px; 35 | text-decoration: none; 36 | margin: 15px 0; 37 | } 38 | 39 | .dowebok { 40 | background: #fff; 41 | border-radius: 10px; 42 | box-shadow: 0 14px 28px rgba(0, 0, 0, .25), 0 10px 10px rgba(0, 0, 0, .22); 43 | position: relative; 44 | overflow: hidden; 45 | width: 768px; 46 | max-width: 100%; 47 | min-height: 480px; 48 | } 49 | 50 | .form-container div { 51 | background: #fff; 52 | display: flex; 53 | flex-direction: column; 54 | padding: 0 50px; 55 | height: 100%; 56 | justify-content: center; 57 | align-items: center; 58 | text-align: center; 59 | } 60 | 61 | .social-container { 62 | margin: 20px 0; 63 | } 64 | 65 | .social-container a { 66 | border: 1px solid #ddd; 67 | border-radius: 50%; 68 | display: inline-flex; 69 | justify-content: center; 70 | align-items: center; 71 | margin: 0 5px; 72 | height: 40px; 73 | width: 40px; 74 | } 75 | 76 | .social-container a:hover { 77 | background-color: #eee; 78 | } 79 | 80 | .form-container input { 81 | background: #eee; 82 | border: none; 83 | padding: 12px 15px; 84 | margin: 8px 0; 85 | width: 100%; 86 | outline: none; 87 | } 88 | 89 | button { 90 | border-radius: 20px; 91 | border: 1px solid #409EFF; 92 | background: rgb(56, 147, 238); 93 | color: #fff; 94 | font-size: 12px; 95 | font-weight: bold; 96 | padding: 12px 45px; 97 | letter-spacing: 1px; 98 | text-transform: uppercase; 99 | transition: transform 80ms ease-in; 100 | cursor: pointer; 101 | } 102 | 103 | button:active { 104 | transform: scale(.95); 105 | } 106 | 107 | button:focus { 108 | outline: none; 109 | } 110 | 111 | button.ghost { 112 | background: transparent; 113 | border-color: #fff; 114 | } 115 | 116 | .form-container { 117 | position: absolute; 118 | top: 0; 119 | height: 100%; 120 | transition: all .6s ease-in-out; 121 | } 122 | 123 | .sign-in-container { 124 | left: 0; 125 | width: 50%; 126 | z-index: 2; 127 | } 128 | 129 | .sign-up-container { 130 | left: 0; 131 | width: 50%; 132 | z-index: 1; 133 | opacity: 0; 134 | } 135 | 136 | .overlay-container { 137 | position: absolute; 138 | top: 0; 139 | left: 50%; 140 | width: 50%; 141 | height: 100%; 142 | overflow: hidden; 143 | transition: transform .6s ease-in-out; 144 | z-index: 100; 145 | } 146 | 147 | .overlay { 148 | background: #409EFF; 149 | background: linear-gradient(to right, #409EFF, #409EFF) no-repeat 0 0 / cover; 150 | color: #fff; 151 | position: relative; 152 | left: -100%; 153 | height: 100%; 154 | width: 200%; 155 | transform: translateY(0); 156 | transition: transform .6s ease-in-out; 157 | } 158 | 159 | .overlay-panel { 160 | position: absolute; 161 | top: 0; 162 | display: flex; 163 | flex-direction: column; 164 | justify-content: center; 165 | align-items: center; 166 | padding: 0 40px; 167 | height: 100%; 168 | width: 50%; 169 | text-align: center; 170 | transform: translateY(0); 171 | transition: transform .6s ease-in-out; 172 | } 173 | 174 | .overlay-right { 175 | right: 0; 176 | transform: translateY(0); 177 | } 178 | 179 | .overlay-left { 180 | transform: translateY(-20%); 181 | } 182 | 183 | /* Move signin to right */ 184 | .dowebok.right-panel-active .sign-in-container { 185 | transform: translateY(100%); 186 | } 187 | 188 | /* Move overlay to left */ 189 | .dowebok.right-panel-active .overlay-container { 190 | transform: translateX(-100%); 191 | } 192 | 193 | /* Bring signup over signin */ 194 | .dowebok.right-panel-active .sign-up-container { 195 | transform: translateX(100%); 196 | opacity: 1; 197 | z-index: 5; 198 | } 199 | 200 | /* Move overlay back to right */ 201 | .dowebok.right-panel-active .overlay { 202 | transform: translateX(50%); 203 | } 204 | 205 | /* Bring back the text to center */ 206 | .dowebok.right-panel-active .overlay-left { 207 | transform: translateY(0); 208 | } 209 | 210 | /* Same effect for right */ 211 | .dowebok.right-panel-active .overlay-right { 212 | transform: translateY(20%); 213 | } 214 | 215 | [v-cloak]{ 216 | display: none; 217 | } 218 | 219 | -------------------------------------------------------------------------------- /src/web/assets/img/fav.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Code-Fight/GoCloud/c4a794c588e17a9ca66715c284cd5ec1161d2d9f/src/web/assets/img/fav.ico -------------------------------------------------------------------------------- /src/web/assets/img/logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | background 10 | 11 | 12 | 13 | Layer 1 14 | GoCloud 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /src/web/assets/img/logo1.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | background 7 | 8 | 9 | 10 | Layer 1 11 | GoCloud 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /src/web/assets/img/t.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Code-Fight/GoCloud/c4a794c588e17a9ca66715c284cd5ec1161d2d9f/src/web/assets/img/t.png -------------------------------------------------------------------------------- /src/web/assets/js/app.js: -------------------------------------------------------------------------------- 1 | //the frontend chunksize must equal backend chunksize 2 | var ChunkSize = 4*1024*1024 3 | 4 | Vue.prototype.$axios = axios 5 | var vm =new Vue({ 6 | delimiters: ['$', '$'],//use the '$$' delimiters,because the '{{' confilct with 'iris' mvc 7 | el: '#app', 8 | data:{ 9 | tableheight:"auto", 10 | shareTableHeight:"auto", 11 | tableData: [], 12 | multipleSelection: [], 13 | uploadTableData:[], 14 | drawer:false, 15 | curRightRow:{}, 16 | parentDir:0, 17 | dialogFormVisible:false, 18 | dir_name:"", 19 | DeletedialogVisible:false, 20 | NavArray:[{ 21 | "ID":0, 22 | "FileName":"首页" 23 | }], 24 | newFileName:"", 25 | newFileNameDialogFormVisible:false, 26 | moveFileDialogFormVisible:false, 27 | moveFileData:[], 28 | moveFileTreeDefaultExpanded:[], 29 | moveFiledefaultProps: { 30 | children: 'children', 31 | label: 'label' 32 | }, 33 | curMoveFileTreeSelected:{}, 34 | shareDialogFormVisible:false, 35 | shareForm: { 36 | name: '分享文件(夹):', 37 | expdate: '7', 38 | haspwd: true, 39 | pwd: '', 40 | link: '', 41 | share_copy_text:'' 42 | }, 43 | share:{ 44 | tableData:[] 45 | }, 46 | createShareButtonText:"创建链接", 47 | mainMenuIndex:1, 48 | shareTableData:[], 49 | appHost:"", 50 | recycleTableData:[], 51 | recycleTableHeight:0, 52 | 53 | }, 54 | methods: { 55 | mouseleftclick(){ 56 | var menu = document.querySelector("#context-menu"); 57 | menu.style.display = 'none'; 58 | this.curRightRow = {} 59 | }, 60 | getHeight(){ 61 | this.tableheight=window.innerHeight-121.511+'px'; //获取浏览器高度减去顶部导航栏 62 | this.shareTableHeight=window.innerHeight-60.511+'px'; //获取浏览器高度减去顶部导航栏 63 | this.recycleTableHeight = window.innerHeight-50.511+'px'; 64 | 65 | }, 66 | handleSelectionChange(val) { 67 | this.multipleSelection = val; 68 | }, 69 | UserCommandHandler(command){ 70 | switch (command) { 71 | case 'exit': 72 | this.logout() 73 | break 74 | case 'info': 75 | vm.$message({ 76 | message: '这个功能还没做。。。。。。', 77 | type: 'success' 78 | }); 79 | break 80 | } 81 | 82 | }, 83 | logout(){ 84 | 85 | this.$axios.get('/login/logout').then(function (response) { 86 | if (response.data.Msg == 'OK'){ 87 | 88 | window.location.href= "/login" 89 | } 90 | }).catch(function (err) { 91 | console.log(err) 92 | }) 93 | }, 94 | recycel_table_expdate_formatter(row, column){ 95 | return diffTime( new Date(row.LastUpdated),new Date()) 96 | }, 97 | table_expdate_formatter(row, column){ 98 | if (row.ShareTime==1){ 99 | return "1 天" 100 | 101 | }else if(row.ShareTime==7){ 102 | return "7 天" 103 | 104 | }else if(row.ShareTime==0){ 105 | return "永久" 106 | } 107 | }, 108 | table_silze_formatter(row, column){ 109 | if (parseInt(row.FileSize) == 0){ 110 | return '' 111 | }else { 112 | if (parseInt(row.FileSize)>=1024*1024*1024){ 113 | Math.round( parseFloat(row.FileSize)/(1024*1024*1024)* 10) / 10 114 | return Math.round( parseFloat(row.FileSize)/(1024*1024*1024) * 10) / 10+'G' 115 | }else if (parseInt(row.FileSize) >= 1024*1024){ 116 | return Math.round( parseFloat(row.FileSize)/(1024*1024) * 10) / 10+'M' 117 | }else if (parseInt(row.FileSize) >= 1024){ 118 | return Math.round( parseFloat(row.FileSize)/(1024) * 10) / 10+'K' 119 | }else { 120 | return row.FileSize+'b' 121 | } 122 | } 123 | }, 124 | table_date_formatter(row, column){ 125 | _obj = row[column.property].toString().split(':') 126 | return row[column.property].toString().replace(":"+_obj[_obj.length-1],"") 127 | }, 128 | fileOnclick(data){ 129 | if (data.row.IsDir){ 130 | //go in dir 131 | this.parentDir = data.row.ID 132 | vm.NavArray.push(data.row) 133 | GetFiles(data.row.ID) 134 | }else { 135 | //maybe pre-show file? 136 | } 137 | }, 138 | preDir(data){ 139 | //go in dir 140 | if (data.ID ==0){ 141 | this.NavArray =[{ 142 | "ID":0, 143 | "FileName":"首页" 144 | }] 145 | this.parentDir = 0 146 | }else if (this.NavArray.length==1){ 147 | this.parentDir = 0 148 | return; 149 | }else { 150 | index =vm.NavArray.indexOf(data) 151 | if (index<0){ 152 | return 153 | } 154 | this.parentDir = vm.NavArray[index].ID 155 | 156 | vm.NavArray.splice(index+1,(vm.NavArray.length)-index) 157 | 158 | } 159 | GetFiles(this.parentDir) 160 | 161 | 162 | 163 | 164 | }, 165 | menuSelected(index,indexPath){ 166 | this.mainMenuIndex = index 167 | if(index==1){ 168 | this.NavArray =[{ 169 | "ID":0, 170 | "FileName":"首页" 171 | }] 172 | this.parentDir = 0 173 | GetFiles(0) 174 | }else if(index==2){ 175 | GetShareFiles() 176 | }else if(index==3){ 177 | GetRecycleBinFiles() 178 | } 179 | 180 | 181 | }, 182 | fileDeleteConfirm(){ 183 | RightMenuDisplayNone() 184 | this.DeletedialogVisible=true 185 | }, 186 | fileDelete(){ 187 | RightMenuDisplayNone() 188 | this.DeletedialogVisible=false 189 | this.$axios.get("/file/delete/"+this.curRightRow.FileQetag+"/"+this.curRightRow.ID 190 | ).then(resp=>{ 191 | if (resp.data.Status == 1){ 192 | GetFiles(this.curRightRow.ParentDir) 193 | }else { 194 | ErrMsg(resp.data.Msg) 195 | } 196 | }) 197 | }, 198 | uploadDelete(data){ 199 | data.row.cancel() 200 | index =this.uploadTableData.indexOf(data.row) 201 | this.uploadTableData.splice(index,1) 202 | 203 | }, 204 | uploadPause(data){ 205 | if (data.row.isUploading()){ 206 | data.row.pause() 207 | }else { 208 | data.row.retry() 209 | } 210 | }, 211 | row_contextmenu(row, column, event) { 212 | this.curRightRow = row 213 | var menu = document.querySelector("#context-menu"); 214 | event.preventDefault(); 215 | if (event.clientY+171 > window.innerHeight){ 216 | menu.style.top = event.clientY -171 + 'px'; 217 | }else { 218 | menu.style.top = event.clientY + 'px'; 219 | } 220 | menu.style.left = event.clientX + 'px'; 221 | 222 | 223 | menu.style.display = 'block'; 224 | }, 225 | fileDownload(){ 226 | RightMenuDisplayNone() 227 | if (this.curRightRow.IsDir==1){ 228 | ErrMsg("暂不支持文件夹下载功能") 229 | return 230 | } 231 | try { 232 | var elemIF = document.createElement("iframe"); 233 | elemIF.src = "/file/downloadfile/"+this.curRightRow.FileName+"?fileqetag="+this.curRightRow.FileQetag; 234 | elemIF.style.display = "none"; 235 | document.body.appendChild(elemIF); 236 | } catch (e) { 237 | ErrMsg(e) 238 | } 239 | }, 240 | OnCreateDir() { 241 | this.dialogFormVisible = false 242 | // alert(this.dir_name) 243 | 244 | this.$axios.get("/file/createdir/"+vm.$data.parentDir+"/"+vm.$data.dir_name). 245 | then(resp=>{ 246 | if (resp.data.Status ==1){ 247 | vm.$data.dir_name = "" 248 | GetFiles(vm.$data.parentDir) 249 | }else { 250 | ErrMsg(resp.data.Msg) 251 | } 252 | }) 253 | }, 254 | preOnRenameFile(){ 255 | this.newFileNameDialogFormVisible = true 256 | RightMenuDisplayNone() 257 | }, 258 | OnRenameFile() { 259 | this.newFileNameDialogFormVisible = false 260 | this.$axios.get("/file/renamefile/"+vm.$data.curRightRow.ID+"/"+vm.$data.newFileName). 261 | then(resp=>{ 262 | if (resp.data.Status ==1){ 263 | vm.$data.newFileName = "" 264 | GetFiles(vm.$data.parentDir) 265 | }else { 266 | ErrMsg(resp.data.Msg) 267 | } 268 | }) 269 | }, 270 | preOnMoveFile(){ 271 | RightMenuDisplayNone() 272 | this.$axios.get("/file/userdirs/"+this.curRightRow.ID). 273 | then(resp=>{ 274 | if (resp.data.Status ==1){ 275 | console.log(resp.data.Data) 276 | this.moveFileDialogFormVisible =true 277 | 278 | 279 | 280 | tmp=[{"id":0,"label":"全部文件","children":resp.data.Data}] 281 | this.moveFileData=tmp 282 | this.moveFileTreeDefaultExpanded.push(0) 283 | 284 | 285 | }else { 286 | ErrMsg(resp.data.Msg) 287 | } 288 | }) 289 | }, 290 | moveFileTreeSelect(data,node,obj){ 291 | this.curMoveFileTreeSelected= data.id 292 | }, 293 | OnMoveFile(){ 294 | this.moveFileDialogFormVisible=false 295 | data =new FormData() 296 | data.append("id",this.curRightRow.ID) 297 | data.append("dir",this.curMoveFileTreeSelected) 298 | 299 | this.$axios.post("/file/movefile",data). 300 | then(resp=>{ 301 | if (resp.data.Status ==1){ 302 | GetFiles(this.parentDir) 303 | }else { 304 | ErrMsg(resp.data.Msg) 305 | } 306 | }) 307 | }, 308 | moveFileFilterNode(value, data){ 309 | if (data.id==value){ 310 | return false 311 | } 312 | return true 313 | }, 314 | preShareFile(){ 315 | if(this.curRightRow.IsDir==1){ 316 | ErrMsg("抱歉,暂时不支持文件夹分享") 317 | return 318 | } 319 | this.createShareButtonText="创建链接" 320 | RightMenuDisplayNone() 321 | this.shareForm.pwd = randomCode() 322 | this.shareForm.link = "" 323 | this.shareDialogFormVisible = true 324 | this.shareForm.name='分享文件(夹): '+this.curRightRow.FileName 325 | }, 326 | OnShareFile(){ 327 | 328 | if(this.shareForm.link.length>0){ 329 | 330 | 331 | 332 | 333 | }else { 334 | data = new FormData() 335 | data.append("user_file_id",this.curRightRow.ID) 336 | data.append("share_pwd",this.shareForm.pwd) 337 | data.append("share_time",this.shareForm.expdate) 338 | 339 | 340 | this.$axios.post("/share/createshare",data) 341 | .then(resp=>{ 342 | if (resp.data.Status ==1){ 343 | this.shareForm.link =window.location.href+"share/"+resp.data.Data.link 344 | this.shareForm.share_copy_text="打开链接:"+this.shareForm.link+" 密码:"+this.shareForm.pwd +" 查看我分享给你的文件。" 345 | this.shareDialogFormVisible = true 346 | }else { 347 | ErrMsg(resp.data.Msg) 348 | } 349 | }).catch(err=>{ 350 | ErrMsg(err) 351 | }) 352 | } 353 | 354 | 355 | }, 356 | cancelShare(props){ 357 | this.$axios.get("/share/cancelshare/"+props.row.ShareId) 358 | .then(resp=>{ 359 | if (resp.data.Status == 1){ 360 | GetShareFiles() 361 | }else { 362 | ErrMsg(resp.data.Msg) 363 | } 364 | }).catch(err=>{ 365 | ErrMsg(err) 366 | }) 367 | } 368 | 369 | 370 | 371 | }, 372 | created: function () { 373 | this.mainMenuIndex=1 374 | window.addEventListener('resize', this.getHeight); 375 | this.getHeight() 376 | GetFiles(0) 377 | 378 | 379 | }, 380 | destroyed:function () { 381 | window.removeEventListener('resize', this.getHeight); 382 | } 383 | }) 384 | 385 | // file download 386 | function download(content,fileName){ 387 | const blob = new Blob([content]) //创建一个类文件对象:Blob对象表示一个不可变的、原始数据的类文件对象 388 | const url = window.URL.createObjectURL(blob)//URL.createObjectURL(object)表示生成一个File对象或Blob对象 389 | let dom = document.createElement('a')//设置一个隐藏的a标签,href为输出流,设置download 390 | dom.style.display = 'none' 391 | dom.href = url 392 | dom.setAttribute('download',fileName)//指示浏览器下载url,而不是导航到它;因此将提示用户将其保存为本地文件 393 | document.body.appendChild(dom) 394 | dom.click() 395 | } 396 | 397 | // get the use file by p 398 | function GetFiles(p) { 399 | p = p||(vm?vm.$data.parentDir:0)||0 400 | axios.get('/file/userindexfiles?p='+p).then(function (response) { 401 | if (response.data.Status == 1){ 402 | if (!response.data.Data){ 403 | vm.tableData=[] 404 | return 405 | } 406 | 407 | vm.tableData = response.data.Data 408 | }else { 409 | alert(response.data.Msg) 410 | } 411 | }).catch(function (err) { 412 | console.log(err) 413 | }) 414 | } 415 | 416 | // get the share file 417 | function GetShareFiles() { 418 | vm.$data.appHost = window.location.href+"share/" 419 | axios.get("/file/usersharefiles/"+username) 420 | .then(resp=>{ 421 | if (resp.data.Status ==1){ 422 | if(!resp.data.Data){ 423 | vm.$data.shareTableData=[] 424 | return 425 | } 426 | vm.$data.shareTableData = resp.data.Data 427 | }else { 428 | ErrMsg(err) 429 | } 430 | }) 431 | .catch(err=>{ 432 | ErrMsg(err) 433 | }) 434 | 435 | } 436 | 437 | // get the recycle bin file 438 | function GetRecycleBinFiles() { 439 | axios.get("/file/userrecyclefiles") 440 | .then(resp=>{ 441 | if (resp.data.Status ==1){ 442 | if(!resp.data.Data){ 443 | vm.$data.recycleTableData=[] 444 | return 445 | } 446 | vm.$data.recycleTableData = resp.data.Data 447 | }else { 448 | ErrMsg(err) 449 | } 450 | }) 451 | .catch(err=>{ 452 | ErrMsg(err) 453 | }) 454 | 455 | } 456 | 457 | //时间差计算 458 | /* 459 | * startDate==>开始时间 460 | * endDate==>结束时间 461 | * 事例:diffTime(data.createTime,new Date()) 462 | * 463 | * */ 464 | function diffTime(startDate,endDate) { 465 | var diff=endDate.getTime() - startDate;//.getTime();//时间差的毫秒数 466 | 467 | //计算出相差天数 468 | var days=Math.floor(diff/(24*3600*1000)); 469 | 470 | //计算出小时数 471 | var leave1=diff%(24*3600*1000); //计算天数后剩余的毫秒数 472 | var hours=Math.floor(leave1/(3600*1000)); 473 | //计算相差分钟数 474 | var leave2=leave1%(3600*1000); //计算小时数后剩余的毫秒数 475 | var minutes=Math.floor(leave2/(60*1000)); 476 | 477 | //计算相差秒数 478 | var leave3=leave2%(60*1000); //计算分钟数后剩余的毫秒数 479 | var seconds=Math.round(leave3/1000); 480 | 481 | var returnStr = seconds + "秒"; 482 | if(minutes>0) { 483 | returnStr = minutes + "分钟";//+ returnStr; 484 | } 485 | if(hours>0) { 486 | returnStr = hours + "小时";// + returnStr; 487 | } 488 | if(days>0) { 489 | returnStr = days + "天" ;//+ returnStr; 490 | } 491 | return returnStr; 492 | } 493 | 494 | 495 | 496 | function RightMenuDisplayNone() { 497 | document.querySelector("#context-menu").style.display = 'none'; 498 | } 499 | 500 | function ErrMsg(msg) { 501 | vm.$message({ 502 | message: msg, 503 | type: 'error', 504 | offset: 100 505 | }); 506 | } 507 | 508 | //ref :https://blog.csdn.net/qq_34292479/article/details/87621491 509 | function randomCode(){ 510 | var arr = ['A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z','a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z','0','1','2','3','4','5','6','7','8','9']; 511 | var idvalue =''; 512 | var n = 4; 513 | for(var i=0;i { 576 | console.log(response.data) 577 | if (parseInt(response.data.Status) ==1){ 578 | file.uploadstatus="success" 579 | GetFiles() 580 | }else { 581 | file.uploadstatus="exception" 582 | } 583 | }) 584 | 585 | 586 | }); 587 | 588 | flow.on('fileProgress',function (file, chunk) { 589 | file.curprogress =Math.ceil( file.progress()*100) 590 | }) 591 | 592 | flow.on('fileError', function(file, message,chunk){ 593 | if (401 === chunk.xhr.status) { 594 | window.location = '/login'; 595 | }else { 596 | ErrMsg("error:"+chunk.xhr.status) 597 | 598 | } 599 | 600 | }); 601 | 602 | 603 | 604 | //qetag offical:https://github.com/qiniu/qetag 605 | //get_qetag_and_upload ,custom function to get qetag ,then upload file 606 | //custom the qetag function by flow.js 607 | // sha1算法 608 | var shA1 = sha1.digest; 609 | function get_qetag_and_upload(file) { 610 | var prefix = 0x16; 611 | var sha1String = []; 612 | 613 | var blockSize = ChunkSize; 614 | var blockCount = 0; 615 | var chunkIndex = 0 616 | 617 | 618 | function SetSha1StringArry(chunk) { 619 | 620 | var fileObj = chunk.fileObj 621 | var blob = webAPIFileRead(fileObj,chunk.startByte,chunk.endByte,fileObj.file.type) 622 | blob.arrayBuffer().then(function (buffer) { 623 | if (buffer == blockSize){ 624 | sha1String.push(shA1(buffer)); 625 | }else { 626 | var bufferSize = buffer.size || buffer.length || buffer.byteLength; 627 | blockCount = Math.ceil(bufferSize / blockSize); 628 | for (var i = 0; i < blockCount; i++) { 629 | //sha1beginTime = +new Date(); 630 | sha1String.push(shA1(buffer.slice(i * blockSize, (i + 1) * blockSize))); 631 | //var sha1endTime = +new Date(); 632 | //sha1CostTime +=sha1endTime-sha1beginTime 633 | 634 | 635 | } 636 | } 637 | 638 | chunkIndex++ 639 | if (chunkIndex < file.chunks.length){ 640 | //Forced synchronous execution to prevent qetag errors 641 | //TODO: can be optimized 642 | SetSha1StringArry(file.chunks[chunkIndex]) 643 | 644 | }else { 645 | //to calc 646 | calcEtag(sha1String,file) 647 | //console.log("sha1共用时"+sha1CostTime+"ms"); 648 | } 649 | }) 650 | 651 | } 652 | // copy and modify by flow.js 653 | function webAPIFileRead(fileObj, startByte, endByte, fileType) { 654 | var function_name = 'slice'; 655 | 656 | if (fileObj.file.slice) 657 | function_name = 'slice'; 658 | else if (fileObj.file.mozSlice) 659 | function_name = 'mozSlice'; 660 | else if (fileObj.file.webkitSlice) 661 | function_name = 'webkitSlice'; 662 | 663 | return (fileObj.file[function_name](startByte, endByte, fileType)); 664 | } 665 | 666 | SetSha1StringArry(file.chunks[chunkIndex]) 667 | // for (var i=0; i< file.chunks.length ;i++){ 668 | // SetSha1StringArry(file.chunks[i]) 669 | // } 670 | //return (calcEtag()); 671 | } 672 | 673 | function calcEtag(sha1,file) { 674 | var prefix = 0x16; 675 | var sha1String = sha1; 676 | 677 | 678 | function concatArr2Uint8(s) {//Array 2 Uint8Array 679 | var tmp = []; 680 | for (var i of s) tmp = tmp.concat(i); 681 | return new Uint8Array(tmp); 682 | } 683 | function Uint8ToBase64(u8Arr, urisafe) {//Uint8Array 2 Base64 684 | var CHUNK_SIZE = 0x8000; //arbitrary number 685 | var index = 0; 686 | var length = u8Arr.length; 687 | var result = ''; 688 | var slice; 689 | while (index < length) { 690 | slice = u8Arr.subarray(index, Math.min(index + CHUNK_SIZE, length)); 691 | result += String.fromCharCode.apply(null, slice); 692 | index += CHUNK_SIZE; 693 | } 694 | return urisafe ? btoa(result).replace(/\//g, '_').replace(/\+/g, '-') : btoa(result); 695 | } 696 | function calcEtag() { 697 | if (!sha1String.length) return 'Fto5o-5ea0sNMlW_75VgGJCv2AcJ'; 698 | var sha1Buffer = concatArr2Uint8(sha1String); 699 | // 如果大于4M,则对各个块的sha1结果再次sha1 700 | // ChunkSize = 4M 701 | if (file.size > ChunkSize) { 702 | prefix = 0x96; 703 | sha1Buffer = shA1(sha1Buffer.buffer); 704 | } else { 705 | sha1Buffer = Array.apply([], sha1Buffer); 706 | } 707 | sha1Buffer = concatArr2Uint8([[prefix], sha1Buffer]); 708 | var _base64 =Uint8ToBase64(sha1Buffer, true); 709 | 710 | //console.log("_base64 :"+_base64) 711 | return _base64 712 | } 713 | var qetag = calcEtag() 714 | file.qetag = qetag 715 | uploadfile(file) 716 | } 717 | 718 | function uploadfile(file) { 719 | 720 | fileSecondsPass(function (succ) { 721 | if (!succ){ 722 | flow.upload() 723 | }else { 724 | //the seconds pass succ 725 | file.isSecond = 1 726 | file.cancel() 727 | GetFiles(vm.$data.parentDir) 728 | } 729 | },file) 730 | 731 | } 732 | //fileSecondsPass if can seconds-pass return true,else false 733 | function fileSecondsPass(f,file) { 734 | var bodyFormData =new FormData() 735 | bodyFormData.append("qetag",file.qetag) 736 | bodyFormData.append("fileName",file.name) 737 | bodyFormData.append("parentDir",vm.$data.parentDir) 738 | axios.post("/file/filesecondspass",bodyFormData) 739 | .then(resp=>{ 740 | if (parseInt(resp.data.Status)==1){ 741 | f(true) 742 | }else { 743 | f(false) 744 | } 745 | }).catch(err=>{ 746 | f(false) 747 | }) 748 | 749 | } 750 | 751 | 752 | -------------------------------------------------------------------------------- /src/web/assets/js/axios.min.js: -------------------------------------------------------------------------------- 1 | /* axios v0.19.2 | (c) 2020 by Matt Zabriskie */ 2 | !function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t():"function"==typeof define&&define.amd?define([],t):"object"==typeof exports?exports.axios=t():e.axios=t()}(this,function(){return function(e){function t(r){if(n[r])return n[r].exports;var o=n[r]={exports:{},id:r,loaded:!1};return e[r].call(o.exports,o,o.exports,t),o.loaded=!0,o.exports}var n={};return t.m=e,t.c=n,t.p="",t(0)}([function(e,t,n){e.exports=n(1)},function(e,t,n){"use strict";function r(e){var t=new s(e),n=i(s.prototype.request,t);return o.extend(n,s.prototype,t),o.extend(n,t),n}var o=n(2),i=n(3),s=n(4),a=n(22),u=n(10),c=r(u);c.Axios=s,c.create=function(e){return r(a(c.defaults,e))},c.Cancel=n(23),c.CancelToken=n(24),c.isCancel=n(9),c.all=function(e){return Promise.all(e)},c.spread=n(25),e.exports=c,e.exports.default=c},function(e,t,n){"use strict";function r(e){return"[object Array]"===j.call(e)}function o(e){return"undefined"==typeof e}function i(e){return null!==e&&!o(e)&&null!==e.constructor&&!o(e.constructor)&&"function"==typeof e.constructor.isBuffer&&e.constructor.isBuffer(e)}function s(e){return"[object ArrayBuffer]"===j.call(e)}function a(e){return"undefined"!=typeof FormData&&e instanceof FormData}function u(e){var t;return t="undefined"!=typeof ArrayBuffer&&ArrayBuffer.isView?ArrayBuffer.isView(e):e&&e.buffer&&e.buffer instanceof ArrayBuffer}function c(e){return"string"==typeof e}function f(e){return"number"==typeof e}function p(e){return null!==e&&"object"==typeof e}function d(e){return"[object Date]"===j.call(e)}function l(e){return"[object File]"===j.call(e)}function h(e){return"[object Blob]"===j.call(e)}function m(e){return"[object Function]"===j.call(e)}function y(e){return p(e)&&m(e.pipe)}function g(e){return"undefined"!=typeof URLSearchParams&&e instanceof URLSearchParams}function v(e){return e.replace(/^\s*/,"").replace(/\s*$/,"")}function x(){return("undefined"==typeof navigator||"ReactNative"!==navigator.product&&"NativeScript"!==navigator.product&&"NS"!==navigator.product)&&("undefined"!=typeof window&&"undefined"!=typeof document)}function w(e,t){if(null!==e&&"undefined"!=typeof e)if("object"!=typeof e&&(e=[e]),r(e))for(var n=0,o=e.length;n=200&&e<300}};u.headers={common:{Accept:"application/json, text/plain, */*"}},i.forEach(["delete","get","head"],function(e){u.headers[e]={}}),i.forEach(["post","put","patch"],function(e){u.headers[e]=i.merge(a)}),e.exports=u},function(e,t,n){"use strict";var r=n(2);e.exports=function(e,t){r.forEach(e,function(n,r){r!==t&&r.toUpperCase()===t.toUpperCase()&&(e[t]=n,delete e[r])})}},function(e,t,n){"use strict";var r=n(2),o=n(13),i=n(5),s=n(16),a=n(19),u=n(20),c=n(14);e.exports=function(e){return new Promise(function(t,f){var p=e.data,d=e.headers;r.isFormData(p)&&delete d["Content-Type"];var l=new XMLHttpRequest;if(e.auth){var h=e.auth.username||"",m=e.auth.password||"";d.Authorization="Basic "+btoa(h+":"+m)}var y=s(e.baseURL,e.url);if(l.open(e.method.toUpperCase(),i(y,e.params,e.paramsSerializer),!0),l.timeout=e.timeout,l.onreadystatechange=function(){if(l&&4===l.readyState&&(0!==l.status||l.responseURL&&0===l.responseURL.indexOf("file:"))){var n="getAllResponseHeaders"in l?a(l.getAllResponseHeaders()):null,r=e.responseType&&"text"!==e.responseType?l.response:l.responseText,i={data:r,status:l.status,statusText:l.statusText,headers:n,config:e,request:l};o(t,f,i),l=null}},l.onabort=function(){l&&(f(c("Request aborted",e,"ECONNABORTED",l)),l=null)},l.onerror=function(){f(c("Network Error",e,null,l)),l=null},l.ontimeout=function(){var t="timeout of "+e.timeout+"ms exceeded";e.timeoutErrorMessage&&(t=e.timeoutErrorMessage),f(c(t,e,"ECONNABORTED",l)),l=null},r.isStandardBrowserEnv()){var g=n(21),v=(e.withCredentials||u(y))&&e.xsrfCookieName?g.read(e.xsrfCookieName):void 0;v&&(d[e.xsrfHeaderName]=v)}if("setRequestHeader"in l&&r.forEach(d,function(e,t){"undefined"==typeof p&&"content-type"===t.toLowerCase()?delete d[t]:l.setRequestHeader(t,e)}),r.isUndefined(e.withCredentials)||(l.withCredentials=!!e.withCredentials),e.responseType)try{l.responseType=e.responseType}catch(t){if("json"!==e.responseType)throw t}"function"==typeof e.onDownloadProgress&&l.addEventListener("progress",e.onDownloadProgress),"function"==typeof e.onUploadProgress&&l.upload&&l.upload.addEventListener("progress",e.onUploadProgress),e.cancelToken&&e.cancelToken.promise.then(function(e){l&&(l.abort(),f(e),l=null)}),void 0===p&&(p=null),l.send(p)})}},function(e,t,n){"use strict";var r=n(14);e.exports=function(e,t,n){var o=n.config.validateStatus;!o||o(n.status)?e(n):t(r("Request failed with status code "+n.status,n.config,null,n.request,n))}},function(e,t,n){"use strict";var r=n(15);e.exports=function(e,t,n,o,i){var s=new Error(e);return r(s,t,n,o,i)}},function(e,t){"use strict";e.exports=function(e,t,n,r,o){return e.config=t,n&&(e.code=n),e.request=r,e.response=o,e.isAxiosError=!0,e.toJSON=function(){return{message:this.message,name:this.name,description:this.description,number:this.number,fileName:this.fileName,lineNumber:this.lineNumber,columnNumber:this.columnNumber,stack:this.stack,config:this.config,code:this.code}},e}},function(e,t,n){"use strict";var r=n(17),o=n(18);e.exports=function(e,t){return e&&!r(t)?o(e,t):t}},function(e,t){"use strict";e.exports=function(e){return/^([a-z][a-z\d\+\-\.]*:)?\/\//i.test(e)}},function(e,t){"use strict";e.exports=function(e,t){return t?e.replace(/\/+$/,"")+"/"+t.replace(/^\/+/,""):e}},function(e,t,n){"use strict";var r=n(2),o=["age","authorization","content-length","content-type","etag","expires","from","host","if-modified-since","if-unmodified-since","last-modified","location","max-forwards","proxy-authorization","referer","retry-after","user-agent"];e.exports=function(e){var t,n,i,s={};return e?(r.forEach(e.split("\n"),function(e){if(i=e.indexOf(":"),t=r.trim(e.substr(0,i)).toLowerCase(),n=r.trim(e.substr(i+1)),t){if(s[t]&&o.indexOf(t)>=0)return;"set-cookie"===t?s[t]=(s[t]?s[t]:[]).concat([n]):s[t]=s[t]?s[t]+", "+n:n}}),s):s}},function(e,t,n){"use strict";var r=n(2);e.exports=r.isStandardBrowserEnv()?function(){function e(e){var t=e;return n&&(o.setAttribute("href",t),t=o.href),o.setAttribute("href",t),{href:o.href,protocol:o.protocol?o.protocol.replace(/:$/,""):"",host:o.host,search:o.search?o.search.replace(/^\?/,""):"",hash:o.hash?o.hash.replace(/^#/,""):"",hostname:o.hostname,port:o.port,pathname:"/"===o.pathname.charAt(0)?o.pathname:"/"+o.pathname}}var t,n=/(msie|trident)/i.test(navigator.userAgent),o=document.createElement("a");return t=e(window.location.href),function(n){var o=r.isString(n)?e(n):n;return o.protocol===t.protocol&&o.host===t.host}}():function(){return function(){return!0}}()},function(e,t,n){"use strict";var r=n(2);e.exports=r.isStandardBrowserEnv()?function(){return{write:function(e,t,n,o,i,s){var a=[];a.push(e+"="+encodeURIComponent(t)),r.isNumber(n)&&a.push("expires="+new Date(n).toGMTString()),r.isString(o)&&a.push("path="+o),r.isString(i)&&a.push("domain="+i),s===!0&&a.push("secure"),document.cookie=a.join("; ")},read:function(e){var t=document.cookie.match(new RegExp("(^|;\\s*)("+e+")=([^;]*)"));return t?decodeURIComponent(t[3]):null},remove:function(e){this.write(e,"",Date.now()-864e5)}}}():function(){return{write:function(){},read:function(){return null},remove:function(){}}}()},function(e,t,n){"use strict";var r=n(2);e.exports=function(e,t){t=t||{};var n={},o=["url","method","params","data"],i=["headers","auth","proxy"],s=["baseURL","url","transformRequest","transformResponse","paramsSerializer","timeout","withCredentials","adapter","responseType","xsrfCookieName","xsrfHeaderName","onUploadProgress","onDownloadProgress","maxContentLength","validateStatus","maxRedirects","httpAgent","httpsAgent","cancelToken","socketPath"];r.forEach(o,function(e){"undefined"!=typeof t[e]&&(n[e]=t[e])}),r.forEach(i,function(o){r.isObject(t[o])?n[o]=r.deepMerge(e[o],t[o]):"undefined"!=typeof t[o]?n[o]=t[o]:r.isObject(e[o])?n[o]=r.deepMerge(e[o]):"undefined"!=typeof e[o]&&(n[o]=e[o])}),r.forEach(s,function(r){"undefined"!=typeof t[r]?n[r]=t[r]:"undefined"!=typeof e[r]&&(n[r]=e[r])});var a=o.concat(i).concat(s),u=Object.keys(t).filter(function(e){return a.indexOf(e)===-1});return r.forEach(u,function(r){"undefined"!=typeof t[r]?n[r]=t[r]:"undefined"!=typeof e[r]&&(n[r]=e[r])}),n}},function(e,t){"use strict";function n(e){this.message=e}n.prototype.toString=function(){return"Cancel"+(this.message?": "+this.message:"")},n.prototype.__CANCEL__=!0,e.exports=n},function(e,t,n){"use strict";function r(e){if("function"!=typeof e)throw new TypeError("executor must be a function.");var t;this.promise=new Promise(function(e){t=e});var n=this;e(function(e){n.reason||(n.reason=new o(e),t(n.reason))})}var o=n(23);r.prototype.throwIfRequested=function(){if(this.reason)throw this.reason},r.source=function(){var e,t=new r(function(t){e=t});return{token:t,cancel:e}},e.exports=r},function(e,t){"use strict";e.exports=function(e){return function(t){return e.apply(null,t)}}}])}); 3 | //# sourceMappingURL=axios.min.map -------------------------------------------------------------------------------- /src/web/assets/js/login.js: -------------------------------------------------------------------------------- 1 | var vm =new Vue({ 2 | el:"#app", 3 | data:{ 4 | dowebok:'dowebok', 5 | pan:'' 6 | }, 7 | methods: { 8 | signInButton(){ 9 | this.pan='right-panel-active' 10 | }, 11 | signUpButton(){ 12 | this.pan='' 13 | }, 14 | signIn() { 15 | var user_name = document.getElementById('signup_user_name').value 16 | var eamil = document.getElementById('signupeamil').value 17 | var password = document.getElementById('signuppassword').value 18 | 19 | 20 | 21 | var data= {"Username":user_name,"Email":eamil,"Userpwd":password} 22 | axios.post('/login/signup',data). 23 | then(function (response) { 24 | if (response.data == 'OK'){ 25 | 26 | vm.$message({ 27 | message: '恭喜你,注册成功', 28 | type: 'success' 29 | }); 30 | vm.signUpButton() 31 | }else { 32 | vm.$message({ 33 | message: '注册错误:'+response.data, 34 | type: 'error' 35 | }); 36 | } 37 | }).catch(function (error) { 38 | console.log(error) 39 | 40 | }) 41 | }, 42 | signUp() { 43 | 44 | var eamil = document.getElementById('eamil').value 45 | var password = document.getElementById('passwd').value 46 | 47 | if (eamil.length ==0 || password.length ==0){ 48 | vm.$message({ 49 | message: '请输入邮箱和密码', 50 | type: 'error' 51 | }); 52 | return 53 | } 54 | 55 | var data= {"Email":eamil,"Userpwd":password} 56 | axios.post('/login/signin',data). 57 | then(function (response) { 58 | if (response.data.Msg == 'OK'){ 59 | 60 | vm.$message({ 61 | message: '恭喜你,登录成功', 62 | type: 'success' 63 | }); 64 | window.location.href= "/" 65 | }else { 66 | vm.$message({ 67 | message: '登录失败:'+response.data.Msg, 68 | type: 'error' 69 | }); 70 | } 71 | }).catch(function (error) { 72 | console.log(error) 73 | }) 74 | } 75 | } 76 | }) 77 | -------------------------------------------------------------------------------- /src/web/assets/js/share.js: -------------------------------------------------------------------------------- 1 | 2 | Vue.prototype.$axios = axios 3 | var vm =new Vue({ 4 | delimiters: ['$', '$'],//use the '$$' delimiters,because the '{{' confilct with 'iris' mvc 5 | el: '#app', 6 | data:{ 7 | file_status:0, 8 | share_pwd:'', 9 | need_pwd:1, 10 | share_id:'', 11 | show_content:0, 12 | is_dir:0, 13 | share_time:"", 14 | create_time:"", 15 | file_name:"", 16 | cur_file:{}, 17 | moveFileDialogFormVisible:false, 18 | moveFileData:[], 19 | moveFileTreeDefaultExpanded:[], 20 | moveFiledefaultProps: { 21 | children: 'children', 22 | label: 'label' 23 | }, 24 | 25 | }, 26 | methods: { 27 | was_login(){ 28 | return username.length >0 29 | }, 30 | onValid(){ 31 | form = new FormData() 32 | form.append("share_id",this.share_id) 33 | form.append("pwd",this.share_pwd) 34 | 35 | this.$axios.post("/share/valid",form) 36 | .then(resp=>{ 37 | if (resp.data.Status==0){ 38 | ErrMsg(resp.data.Msg) 39 | return 40 | } 41 | this.show_content = 1 42 | if(resp.data.Data.IsDir==0){ 43 | //is file,show 44 | this.is_dir = 0 45 | this.create_time = resp.data.Data.CreateAt 46 | this.file_name = resp.data.Data.FileName 47 | this.cur_file = resp.data.Data 48 | if (resp.data.Data.ShareTime==1){ 49 | this.share_time = "1 天" 50 | 51 | }else if(resp.data.Data.ShareTime==7){ 52 | this.share_time = "7 天" 53 | 54 | }else { 55 | this.share_time = "永久" 56 | } 57 | 58 | }else { 59 | //is dir 60 | //to get the dir content 61 | this.is_dir = 1 62 | 63 | } 64 | 65 | }).catch(err=>{ 66 | ErrMsg(err) 67 | }) 68 | }, 69 | mouseleftclick(){ 70 | var menu = document.querySelector("#context-menu"); 71 | menu.style.display = 'none'; 72 | this.curRightRow = {} 73 | }, 74 | getHeight(){ 75 | this.tableheight=window.innerHeight-121.511+'px'; //获取浏览器高度减去顶部导航栏 76 | }, 77 | handleSelectionChange(val) { 78 | this.multipleSelection = val; 79 | }, 80 | UserCommandHandler(command){ 81 | switch (command) { 82 | case 'exit': 83 | this.logout() 84 | break 85 | case 'info': 86 | vm.$message({ 87 | message: '这个功能还没做。。。。。。', 88 | type: 'success' 89 | }); 90 | break 91 | } 92 | 93 | }, 94 | logout(){ 95 | 96 | this.$axios.get('/login/logout').then(function (response) { 97 | if (response.data.Msg == 'OK'){ 98 | 99 | window.location.href= "/login" 100 | } 101 | }).catch(function (err) { 102 | console.log(err) 103 | }) 104 | }, 105 | table_silze_formatter(row, column){ 106 | if (parseInt(row.FileSize) == 0){ 107 | return '' 108 | }else { 109 | if (parseInt(row.FileSize)>=1024*1024*1024){ 110 | Math.round( parseFloat(row.FileSize)/(1024*1024*1024)* 10) / 10 111 | return Math.round( parseFloat(row.FileSize)/(1024*1024*1024) * 10) / 10+'G' 112 | }else if (parseInt(row.FileSize) >= 1024*1024){ 113 | return Math.round( parseFloat(row.FileSize)/(1024*1024) * 10) / 10+'M' 114 | }else if (parseInt(row.FileSize) >= 1024){ 115 | return Math.round( parseFloat(row.FileSize)/(1024) * 10) / 10+'K' 116 | }else { 117 | return row.FileSize+'b' 118 | } 119 | } 120 | }, 121 | table_date_formatter(row, column){ 122 | _obj = row.LastUpdated.toString().split(':') 123 | return row.LastUpdated.toString().replace(":"+_obj[_obj.length-1],"") 124 | }, 125 | fileOnclick(data){ 126 | if (data.row.IsDir){ 127 | //go in dir 128 | this.parentDir = data.row.ID 129 | vm.NavArray.push(data.row) 130 | GetFiles(data.row.ID) 131 | }else { 132 | //maybe pre-show file? 133 | } 134 | }, 135 | preDir(data){ 136 | //go in dir 137 | if (data.ID ==0){ 138 | this.NavArray =[{ 139 | "ID":0, 140 | "FileName":"首页" 141 | }] 142 | this.parentDir = 0 143 | }else if (this.NavArray.length==1){ 144 | this.parentDir = 0 145 | return; 146 | }else { 147 | index =vm.NavArray.indexOf(data) 148 | if (index<0){ 149 | return 150 | } 151 | this.parentDir = vm.NavArray[index].ID 152 | 153 | vm.NavArray.splice(index+1,(vm.NavArray.length)-index) 154 | 155 | } 156 | GetFiles(this.parentDir) 157 | 158 | 159 | 160 | 161 | }, 162 | allfile(index,indexPath){ 163 | 164 | if(index==1){ 165 | this.NavArray =[{ 166 | "ID":0, 167 | "FileName":"首页" 168 | }] 169 | this.parentDir = 0 170 | GetFiles(0) 171 | } 172 | 173 | 174 | }, 175 | fileDeleteConfirm(){ 176 | RightMenuDisplayNone() 177 | this.DeletedialogVisible=true 178 | }, 179 | fileDelete(){ 180 | RightMenuDisplayNone() 181 | this.DeletedialogVisible=false 182 | this.$axios.get("/file/delete/"+this.curRightRow.FileQetag+"/"+this.curRightRow.ID 183 | ).then(resp=>{ 184 | if (resp.data.Status == 1){ 185 | GetFiles(this.curRightRow.ParentDir) 186 | }else { 187 | ErrMsg(resp.data.Msg) 188 | } 189 | }) 190 | }, 191 | uploadDelete(data){ 192 | data.row.cancel() 193 | index =this.uploadTableData.indexOf(data.row) 194 | this.uploadTableData.splice(index,1) 195 | 196 | }, 197 | uploadPause(data){ 198 | if (data.row.isUploading()){ 199 | data.row.pause() 200 | }else { 201 | data.row.retry() 202 | } 203 | }, 204 | row_contextmenu(row, column, event) { 205 | this.curRightRow = row 206 | var menu = document.querySelector("#context-menu"); 207 | event.preventDefault(); 208 | if (event.clientY+171 > window.innerHeight){ 209 | menu.style.top = event.clientY -171 + 'px'; 210 | }else { 211 | menu.style.top = event.clientY + 'px'; 212 | } 213 | menu.style.left = event.clientX + 'px'; 214 | 215 | 216 | menu.style.display = 'block'; 217 | }, 218 | fileDownload(){ 219 | 220 | if (this.cur_file.IsDir==1){ 221 | ErrMsg("暂不支持文件夹下载功能") 222 | return 223 | } 224 | try { 225 | var elemIF = document.createElement("iframe"); 226 | elemIF.src = "/share/downloadfile/"+this.cur_file.FileName+"?share_id="+this.cur_file.ShareId+"&share_pwd="+this.share_pwd; 227 | elemIF.style.display = "none"; 228 | document.body.appendChild(elemIF); 229 | } catch (e) { 230 | ErrMsg(e) 231 | } 232 | }, 233 | OnCreateDir() { 234 | this.dialogFormVisible = false 235 | // alert(this.dir_name) 236 | 237 | this.$axios.get("/file/createdir/"+vm.$data.parentDir+"/"+vm.$data.dir_name). 238 | then(resp=>{ 239 | if (resp.data.Status ==1){ 240 | vm.$data.dir_name = "" 241 | GetFiles(vm.$data.parentDir) 242 | }else { 243 | ErrMsg(resp.data.Msg) 244 | } 245 | }) 246 | }, 247 | preOnRenameFile(){ 248 | this.newFileNameDialogFormVisible = true 249 | RightMenuDisplayNone() 250 | }, 251 | OnRenameFile() { 252 | this.newFileNameDialogFormVisible = false 253 | this.$axios.get("/file/renamefile/"+vm.$data.curRightRow.ID+"/"+vm.$data.newFileName). 254 | then(resp=>{ 255 | if (resp.data.Status ==1){ 256 | vm.$data.newFileName = "" 257 | GetFiles(vm.$data.parentDir) 258 | }else { 259 | ErrMsg(resp.data.Msg) 260 | } 261 | }) 262 | }, 263 | preOnMoveFile(){ 264 | 265 | this.$axios.get("/file/userdirs/"+this.cur_file.UserFileId). 266 | then(resp=>{ 267 | if (resp.data.Status ==1){ 268 | console.log(resp.data.Data) 269 | this.moveFileDialogFormVisible =true 270 | 271 | 272 | 273 | tmp=[{"id":0,"label":"全部文件","children":resp.data.Data}] 274 | this.moveFileData=tmp 275 | this.moveFileTreeDefaultExpanded.push(0) 276 | 277 | 278 | }else { 279 | ErrMsg(resp.data.Msg) 280 | } 281 | }) 282 | }, 283 | moveFileTreeSelect(data,node,obj){ 284 | this.curMoveFileTreeSelected= data.id 285 | }, 286 | OnMoveFile(){ 287 | this.moveFileDialogFormVisible=false 288 | data =new FormData() 289 | data.append("share_id",this.share_id) 290 | data.append("share_pwd",this.share_pwd) 291 | data.append("dir",this.curMoveFileTreeSelected) 292 | 293 | this.$axios.post("/share/savefile",data). 294 | then(resp=>{ 295 | if (resp.data.Status ==1){ 296 | vm.$message({ 297 | message: "文件保存成功", 298 | type: 'success', 299 | offset: 100 300 | }); 301 | }else { 302 | ErrMsg(resp.data.Msg) 303 | } 304 | }) 305 | } 306 | }, 307 | created: function () { 308 | window.addEventListener('resize', this.getHeight); 309 | this.getHeight() 310 | this.share_id = document.querySelector("#share_id").value 311 | if (this.share_id.length==0){ 312 | return 313 | } 314 | this.$axios.get("/share/file/"+this.share_id). 315 | then(resp=>{ 316 | if (resp.data.Status ==1){ 317 | this.file_status = 1 318 | if(resp.data.Data.pwd==1){ 319 | this.need_pwd = 1 320 | }else { 321 | this.need_pwd = 0 322 | } 323 | }else{ 324 | //file status err 325 | this.file_status = 0 326 | } 327 | }).catch(err=>{ 328 | ErrMsg(err) 329 | }) 330 | 331 | 332 | }, 333 | destroyed:function () { 334 | window.removeEventListener('resize', this.getHeight); 335 | } 336 | }) 337 | 338 | // file download 339 | function download(content,fileName){ 340 | const blob = new Blob([content]) //创建一个类文件对象:Blob对象表示一个不可变的、原始数据的类文件对象 341 | const url = window.URL.createObjectURL(blob)//URL.createObjectURL(object)表示生成一个File对象或Blob对象 342 | let dom = document.createElement('a')//设置一个隐藏的a标签,href为输出流,设置download 343 | dom.style.display = 'none' 344 | dom.href = url 345 | dom.setAttribute('download',fileName)//指示浏览器下载url,而不是导航到它;因此将提示用户将其保存为本地文件 346 | document.body.appendChild(dom) 347 | dom.click() 348 | } 349 | 350 | // get the use file by p 351 | function GetFiles(p) { 352 | p = p||(vm?vm.$data.parentDir:0)||0 353 | axios.get('/file/userindexfiles?p='+p).then(function (response) { 354 | if (response.data.Status == 1){ 355 | if (!response.data.Data){ 356 | vm.tableData=[] 357 | return 358 | } 359 | 360 | vm.tableData = response.data.Data 361 | }else { 362 | alert(response.data.Msg) 363 | } 364 | }).catch(function (err) { 365 | console.log(err) 366 | }) 367 | } 368 | 369 | 370 | function RightMenuDisplayNone() { 371 | document.querySelector("#context-menu").style.display = 'none'; 372 | } 373 | 374 | function ErrMsg(msg) { 375 | vm.$message({ 376 | message: msg, 377 | type: 'error', 378 | offset: 100 379 | }); 380 | } 381 | -------------------------------------------------------------------------------- /src/web/assets/libs/clipboard/clipboard.min.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * clipboard.js v2.0.6 3 | * https://clipboardjs.com/ 4 | * 5 | * Licensed MIT © Zeno Rocha 6 | */ 7 | !function(t,e){"object"==typeof exports&&"object"==typeof module?module.exports=e():"function"==typeof define&&define.amd?define([],e):"object"==typeof exports?exports.ClipboardJS=e():t.ClipboardJS=e()}(this,function(){return o={},r.m=n=[function(t,e){t.exports=function(t){var e;if("SELECT"===t.nodeName)t.focus(),e=t.value;else if("INPUT"===t.nodeName||"TEXTAREA"===t.nodeName){var n=t.hasAttribute("readonly");n||t.setAttribute("readonly",""),t.select(),t.setSelectionRange(0,t.value.length),n||t.removeAttribute("readonly"),e=t.value}else{t.hasAttribute("contenteditable")&&t.focus();var o=window.getSelection(),r=document.createRange();r.selectNodeContents(t),o.removeAllRanges(),o.addRange(r),e=o.toString()}return e}},function(t,e){function n(){}n.prototype={on:function(t,e,n){var o=this.e||(this.e={});return(o[t]||(o[t]=[])).push({fn:e,ctx:n}),this},once:function(t,e,n){var o=this;function r(){o.off(t,r),e.apply(n,arguments)}return r._=e,this.on(t,r,n)},emit:function(t){for(var e=[].slice.call(arguments,1),n=((this.e||(this.e={}))[t]||[]).slice(),o=0,r=n.length;o-1&&a.splice(c,1)}function i(a,b){return"function"==typeof a&&(b=Array.prototype.slice.call(arguments),a=a.apply(null,b.slice(1))),a}function j(a,b){setTimeout(a.bind(b),0)}function k(a,b){return l(arguments,function(b){b!==a&&l(b,function(b,c){a[c]=b})}),a}function l(a,b,c){if(a){var d;if("undefined"!=typeof a.length){for(d=0;d1&&"pending"===a.chunks[a.chunks.length-1].status()?(a.chunks[a.chunks.length-1].send(),b=!0,!1):void 0}),b))return b;if(l(this.files,function(a){if(a.paused||l(a.chunks,function(a){if("pending"===a.status())return a.send(),b=!0,!1}),b)return!1}),b)return!0;var c=!1;return l(this.files,function(a){if(!a.isComplete())return c=!0,!1}),c||a||j(function(){this.fire("complete")},this),!1},assignBrowse:function(a,c,d,e){a instanceof Element&&(a=[a]),l(a,function(a){var f;"INPUT"===a.tagName&&"file"===a.type?f=a:(f=b.createElement("input"),f.setAttribute("type","file"),k(f.style,{visibility:"hidden",position:"absolute",width:"1px",height:"1px"}),a.appendChild(f),a.addEventListener("click",function(){f.click()},!1)),this.opts.singleFile||d||f.setAttribute("multiple","multiple"),c&&f.setAttribute("webkitdirectory","webkitdirectory"),l(e,function(a,b){f.setAttribute(b,a)});var g=this;f.addEventListener("change",function(a){a.target.value&&(g.addFiles(a.target.files,a),a.target.value="")},!1)},this)},assignDrop:function(a){"undefined"==typeof a.length&&(a=[a]),l(a,function(a){a.addEventListener("dragover",this.preventEvent,!1),a.addEventListener("dragenter",this.preventEvent,!1),a.addEventListener("drop",this.onDrop,!1)},this)},unAssignDrop:function(a){"undefined"==typeof a.length&&(a=[a]),l(a,function(a){a.removeEventListener("dragover",this.preventEvent),a.removeEventListener("dragenter",this.preventEvent),a.removeEventListener("drop",this.onDrop)},this)},isUploading:function(){var a=!1;return l(this.files,function(b){if(b.isUploading())return a=!0,!1}),a},_shouldUploadNext:function(){var a=0,b=!0,c=this.opts.simultaneousUploads;return l(this.files,function(d){l(d.chunks,function(d){if("uploading"===d.status()&&(a++,a>=c))return b=!1,!1})}),b&&a},upload:function(){var a=this._shouldUploadNext();if(a!==!1){this.fire("uploadStart");for(var b=!1,c=1;c<=this.opts.simultaneousUploads-a;c++)b=this.uploadNextChunk(!0)||b;b||j(function(){this.fire("complete")},this)}},resume:function(){l(this.files,function(a){a.isComplete()||a.resume()})},pause:function(){l(this.files,function(a){a.pause()})},cancel:function(){for(var a=this.files.length-1;a>=0;a--)this.files[a].cancel()},progress:function(){var a=0,b=0;return l(this.files,function(c){a+=c.progress()*c.size,b+=c.size}),b>0?a/b:0},addFile:function(a,b){this.addFiles([a],b)},addFiles:function(a,b){var c=[];l(a,function(a){if((!m||m&&a.size>0)&&(a.size%4096!==0||"."!==a.name&&"."!==a.fileName)){var d=this.generateUniqueIdentifier(a);if(this.opts.allowDuplicateUploads||!this.getFromUniqueIdentifier(d)){var f=new e(this,a,d);this.fire("fileAdded",f,b)&&c.push(f)}}},this),this.fire("filesAdded",c,b)&&(l(c,function(a){this.opts.singleFile&&this.files.length>0&&this.removeFile(this.files[0]),this.files.push(a)},this),this.fire("filesSubmitted",c,b))},removeFile:function(a){for(var b=this.files.length-1;b>=0;b--)this.files[b]===a&&(this.files.splice(b,1),a.abort(),this.fire("fileRemoved",a))},getFromUniqueIdentifier:function(a){var b=!1;return l(this.files,function(c){c.uniqueIdentifier===a&&(b=c)}),b},getSize:function(){var a=0;return l(this.files,function(b){a+=b.size}),a},sizeUploaded:function(){var a=0;return l(this.files,function(b){a+=b.sizeUploaded()}),a},timeRemaining:function(){var a=0,b=0;return l(this.files,function(c){c.paused||c.error||(a+=c.size-c.sizeUploaded(),b+=c.averageSpeed)}),a&&!b?Number.POSITIVE_INFINITY:a||b?Math.floor(a/b):0}},e.prototype={measureSpeed:function(){var a=Date.now()-this._lastProgressCallback;if(a){var b=this.flowObj.opts.speedSmoothingFactor,c=this.sizeUploaded();this.currentSpeed=Math.max((c-this._prevUploadedSize)/a*1e3,0),this.averageSpeed=b*this.currentSpeed+(1-b)*this.averageSpeed,this._prevUploadedSize=c}},chunkEvent:function(a,b,c){switch(b){case"progress":if(Date.now()-this._lastProgressCallback.9999?1:b),this._prevProgress},isUploading:function(){var a=!1;return l(this.chunks,function(b){if("uploading"===b.status())return a=!0,!1}),a},isComplete:function(){var a=!1;return l(this.chunks,function(b){var c=b.status();if("pending"===c||"uploading"===c||"reading"===c||1===b.preprocessState||1===b.readState)return a=!0,!1}),!a},sizeUploaded:function(){var a=0;return l(this.chunks,function(b){a+=b.sizeUploaded()}),a},timeRemaining:function(){if(this.paused||this.error)return 0;var a=this.size-this.sizeUploaded();return a&&!this.averageSpeed?Number.POSITIVE_INFINITY:a||this.averageSpeed?Math.floor(a/this.averageSpeed):0},getType:function(){return this.file.type&&this.file.type.split("/")[1]},getExtension:function(){return this.name.substr((~-this.name.lastIndexOf(".")>>>0)+2).toLowerCase()}},g.prototype={getParams:function(){return{flowChunkNumber:this.offset+1,flowChunkSize:this.chunkSize,flowCurrentChunkSize:this.endByte-this.startByte,flowTotalSize:this.fileObj.size,flowIdentifier:this.fileObj.uniqueIdentifier,flowFilename:this.fileObj.name,flowRelativePath:this.fileObj.relativePath,flowTotalChunks:this.fileObj.chunks.length}},getTarget:function(a,b){return 0==b.length?a:(a+=a.indexOf("?")<0?"?":"&",a+b.join("&"))},test:function(){this.xhr=new XMLHttpRequest,this.xhr.addEventListener("load",this.testHandler,!1),this.xhr.addEventListener("error",this.testHandler,!1);var a=i(this.flowObj.opts.testMethod,this.fileObj,this),b=this.prepareXhrRequest(a,!0);this.xhr.send(b)},preprocessFinished:function(){this.endByte=this.computeEndByte(),this.preprocessState=2,this.send()},readFinished:function(a){this.readState=2,this.bytes=a,this.send()},send:function(){var a=this.flowObj.opts.preprocess,b=this.flowObj.opts.readFileFn;if("function"==typeof a)switch(this.preprocessState){case 0:return this.preprocessState=1,void a(this);case 1:return}switch(this.readState){case 0:return this.readState=1,void b(this.fileObj,this.startByte,this.endByte,this.fileObj.file.type,this);case 1:return}if(this.flowObj.opts.testChunks&&!this.tested)return void this.test();this.loaded=0,this.total=0,this.pendingRetry=!1,this.xhr=new XMLHttpRequest,this.xhr.upload.addEventListener("progress",this.progressHandler,!1),this.xhr.addEventListener("load",this.doneHandler,!1),this.xhr.addEventListener("error",this.doneHandler,!1);var c=i(this.flowObj.opts.uploadMethod,this.fileObj,this),d=this.prepareXhrRequest(c,!1,this.flowObj.opts.method,this.bytes),e=this.flowObj.opts.changeRawDataBeforeSend;"function"==typeof e&&(d=e(this,d)),this.xhr.send(d)},abort:function(){var a=this.xhr;this.xhr=null,a&&a.abort()},status:function(a){return 1===this.readState?"reading":this.pendingRetry||1===this.preprocessState?"uploading":this.xhr?this.xhr.readyState<4?"uploading":this.flowObj.opts.successStatuses.indexOf(this.xhr.status)>-1?"success":this.flowObj.opts.permanentErrors.indexOf(this.xhr.status)>-1||!a&&this.retries>=this.flowObj.opts.maxChunkRetries?"error":(this.abort(),"pending"):"pending"},message:function(){return this.xhr?this.xhr.responseText:""},progress:function(){if(this.pendingRetry)return 0;var a=this.status();return"success"===a||"error"===a?1:"pending"===a?0:this.total>0?this.loaded/this.total:0},sizeUploaded:function(){var a=this.endByte-this.startByte;return"success"!==this.status()&&(a=this.progress()*a),a},prepareXhrRequest:function(a,b,c,d){var e=i(this.flowObj.opts.query,this.fileObj,this,b);e=k(e||{},this.getParams());var f=i(this.flowObj.opts.target,this.fileObj,this,b),g=null;if("GET"===a||"octet"===c){var h=[];l(e,function(a,b){h.push([encodeURIComponent(b),encodeURIComponent(a)].join("="))}),f=this.getTarget(f,h),g=d||null}else g=new FormData,l(e,function(a,b){g.append(b,a)}),"undefined"!=typeof d&&g.append(this.flowObj.opts.fileParameterName,d,this.fileObj.file.name);return this.xhr.open(a,f,!0),this.xhr.withCredentials=this.flowObj.opts.withCredentials,l(i(this.flowObj.opts.headers,this.fileObj,this,b),function(a,b){this.xhr.setRequestHeader(b,a)},this),g}},d.evalOpts=i,d.extend=k,d.each=l,d.FlowFile=e,d.FlowChunk=g,d.version="2.14.0","object"==typeof module&&module&&"object"==typeof module.exports?module.exports=d:(a.Flow=d,"function"==typeof define&&define.amd&&define("flow",[],function(){return d}))}("undefined"!=typeof window&&window,"undefined"!=typeof document&&document); -------------------------------------------------------------------------------- /src/web/assets/libs/qetag/qetag.js: -------------------------------------------------------------------------------- 1 | 2 | //offiical : https://github.com/qiniu/qetag/ 3 | 4 | 5 | // ref: https://www.jianshu.com/p/3785fc314fc5 6 | 7 | function getEtag(buffer, callback) { 8 | // sha1算法 9 | var shA1 = sha1.digest; 10 | 11 | // 以4M为单位分割 12 | var blockSize = 4 * 1024 * 1024; 13 | var sha1String = []; 14 | var prefix = 0x16; 15 | var blockCount = 0; 16 | 17 | var bufferSize = buffer.size || buffer.length || buffer.byteLength; 18 | blockCount = Math.ceil(bufferSize / blockSize); 19 | 20 | for (var i = 0; i < blockCount; i++) { 21 | sha1String.push(shA1(buffer.slice(i * blockSize, (i + 1) * blockSize))); 22 | } 23 | function concatArr2Uint8(s) {//Array 2 Uint8Array 24 | var tmp = []; 25 | for (var i of s) tmp = tmp.concat(i); 26 | return new Uint8Array(tmp); 27 | } 28 | function Uint8ToBase64(u8Arr, urisafe) {//Uint8Array 2 Base64 29 | var CHUNK_SIZE = 0x8000; //arbitrary number 30 | var index = 0; 31 | var length = u8Arr.length; 32 | var result = ''; 33 | var slice; 34 | while (index < length) { 35 | slice = u8Arr.subarray(index, Math.min(index + CHUNK_SIZE, length)); 36 | result += String.fromCharCode.apply(null, slice); 37 | index += CHUNK_SIZE; 38 | } 39 | return urisafe ? btoa(result).replace(/\//g, '_').replace(/\+/g, '-') : btoa(result); 40 | } 41 | function calcEtag() { 42 | if (!sha1String.length) return 'Fto5o-5ea0sNMlW_75VgGJCv2AcJ'; 43 | var sha1Buffer = concatArr2Uint8(sha1String); 44 | // 如果大于4M,则对各个块的sha1结果再次sha1 45 | if (blockCount > 1) { 46 | prefix = 0x96; 47 | sha1Buffer = shA1(sha1Buffer.buffer); 48 | } else { 49 | sha1Buffer = Array.apply([], sha1Buffer); 50 | } 51 | sha1Buffer = concatArr2Uint8([[prefix], sha1Buffer]); 52 | return Uint8ToBase64(sha1Buffer, true); 53 | } 54 | return (calcEtag()); 55 | } -------------------------------------------------------------------------------- /src/web/assets/libs/qetag/sha1.min.js: -------------------------------------------------------------------------------- 1 | /* 2 | * [js-sha1]{@link https://github.com/emn178/js-sha1} 3 | * 4 | * @version 0.6.0 5 | * @author Chen, Yi-Cyuan [emn178@gmail.com] 6 | * @copyright Chen, Yi-Cyuan 2014-2017 7 | * @license MIT 8 | */ 9 | !function(){"use strict";function t(t){t?(f[0]=f[16]=f[1]=f[2]=f[3]=f[4]=f[5]=f[6]=f[7]=f[8]=f[9]=f[10]=f[11]=f[12]=f[13]=f[14]=f[15]=0,this.blocks=f):this.blocks=[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],this.h0=1732584193,this.h1=4023233417,this.h2=2562383102,this.h3=271733878,this.h4=3285377520,this.block=this.start=this.bytes=this.hBytes=0,this.finalized=this.hashed=!1,this.first=!0}var h="object"==typeof window?window:{},s=!h.JS_SHA1_NO_NODE_JS&&"object"==typeof process&&process.versions&&process.versions.node;s&&(h=global);var i=!h.JS_SHA1_NO_COMMON_JS&&"object"==typeof module&&module.exports,e="function"==typeof define&&define.amd,r="0123456789abcdef".split(""),o=[-2147483648,8388608,32768,128],n=[24,16,8,0],a=["hex","array","digest","arrayBuffer"],f=[],u=function(h){return function(s){return new t(!0).update(s)[h]()}},c=function(){var h=u("hex");s&&(h=p(h)),h.create=function(){return new t},h.update=function(t){return h.create().update(t)};for(var i=0;i>2]|=t[r]<>2]|=i<>2]|=(192|i>>6)<>2]|=(128|63&i)<=57344?(a[e>>2]|=(224|i>>12)<>2]|=(128|i>>6&63)<>2]|=(128|63&i)<>2]|=(240|i>>18)<>2]|=(128|i>>12&63)<>2]|=(128|i>>6&63)<>2]|=(128|63&i)<=64?(this.block=a[16],this.start=e-64,this.hash(),this.hashed=!0):this.start=e}return this.bytes>4294967295&&(this.hBytes+=this.bytes/4294967296<<0,this.bytes=this.bytes%4294967296),this}},t.prototype.finalize=function(){if(!this.finalized){this.finalized=!0;var t=this.blocks,h=this.lastByteIndex;t[16]=this.block,t[h>>2]|=o[3&h],this.block=t[16],h>=56&&(this.hashed||this.hash(),t[0]=this.block,t[16]=t[1]=t[2]=t[3]=t[4]=t[5]=t[6]=t[7]=t[8]=t[9]=t[10]=t[11]=t[12]=t[13]=t[14]=t[15]=0),t[14]=this.hBytes<<3|this.bytes>>>29,t[15]=this.bytes<<3,this.hash()}},t.prototype.hash=function(){var t,h,s=this.h0,i=this.h1,e=this.h2,r=this.h3,o=this.h4,n=this.blocks;for(t=16;t<80;++t)h=n[t-3]^n[t-8]^n[t-14]^n[t-16],n[t]=h<<1|h>>>31;for(t=0;t<20;t+=5)s=(h=(i=(h=(e=(h=(r=(h=(o=(h=s<<5|s>>>27)+(i&e|~i&r)+o+1518500249+n[t]<<0)<<5|o>>>27)+(s&(i=i<<30|i>>>2)|~s&e)+r+1518500249+n[t+1]<<0)<<5|r>>>27)+(o&(s=s<<30|s>>>2)|~o&i)+e+1518500249+n[t+2]<<0)<<5|e>>>27)+(r&(o=o<<30|o>>>2)|~r&s)+i+1518500249+n[t+3]<<0)<<5|i>>>27)+(e&(r=r<<30|r>>>2)|~e&o)+s+1518500249+n[t+4]<<0,e=e<<30|e>>>2;for(;t<40;t+=5)s=(h=(i=(h=(e=(h=(r=(h=(o=(h=s<<5|s>>>27)+(i^e^r)+o+1859775393+n[t]<<0)<<5|o>>>27)+(s^(i=i<<30|i>>>2)^e)+r+1859775393+n[t+1]<<0)<<5|r>>>27)+(o^(s=s<<30|s>>>2)^i)+e+1859775393+n[t+2]<<0)<<5|e>>>27)+(r^(o=o<<30|o>>>2)^s)+i+1859775393+n[t+3]<<0)<<5|i>>>27)+(e^(r=r<<30|r>>>2)^o)+s+1859775393+n[t+4]<<0,e=e<<30|e>>>2;for(;t<60;t+=5)s=(h=(i=(h=(e=(h=(r=(h=(o=(h=s<<5|s>>>27)+(i&e|i&r|e&r)+o-1894007588+n[t]<<0)<<5|o>>>27)+(s&(i=i<<30|i>>>2)|s&e|i&e)+r-1894007588+n[t+1]<<0)<<5|r>>>27)+(o&(s=s<<30|s>>>2)|o&i|s&i)+e-1894007588+n[t+2]<<0)<<5|e>>>27)+(r&(o=o<<30|o>>>2)|r&s|o&s)+i-1894007588+n[t+3]<<0)<<5|i>>>27)+(e&(r=r<<30|r>>>2)|e&o|r&o)+s-1894007588+n[t+4]<<0,e=e<<30|e>>>2;for(;t<80;t+=5)s=(h=(i=(h=(e=(h=(r=(h=(o=(h=s<<5|s>>>27)+(i^e^r)+o-899497514+n[t]<<0)<<5|o>>>27)+(s^(i=i<<30|i>>>2)^e)+r-899497514+n[t+1]<<0)<<5|r>>>27)+(o^(s=s<<30|s>>>2)^i)+e-899497514+n[t+2]<<0)<<5|e>>>27)+(r^(o=o<<30|o>>>2)^s)+i-899497514+n[t+3]<<0)<<5|i>>>27)+(e^(r=r<<30|r>>>2)^o)+s-899497514+n[t+4]<<0,e=e<<30|e>>>2;this.h0=this.h0+s<<0,this.h1=this.h1+i<<0,this.h2=this.h2+e<<0,this.h3=this.h3+r<<0,this.h4=this.h4+o<<0},t.prototype.hex=function(){this.finalize();var t=this.h0,h=this.h1,s=this.h2,i=this.h3,e=this.h4;return r[t>>28&15]+r[t>>24&15]+r[t>>20&15]+r[t>>16&15]+r[t>>12&15]+r[t>>8&15]+r[t>>4&15]+r[15&t]+r[h>>28&15]+r[h>>24&15]+r[h>>20&15]+r[h>>16&15]+r[h>>12&15]+r[h>>8&15]+r[h>>4&15]+r[15&h]+r[s>>28&15]+r[s>>24&15]+r[s>>20&15]+r[s>>16&15]+r[s>>12&15]+r[s>>8&15]+r[s>>4&15]+r[15&s]+r[i>>28&15]+r[i>>24&15]+r[i>>20&15]+r[i>>16&15]+r[i>>12&15]+r[i>>8&15]+r[i>>4&15]+r[15&i]+r[e>>28&15]+r[e>>24&15]+r[e>>20&15]+r[e>>16&15]+r[e>>12&15]+r[e>>8&15]+r[e>>4&15]+r[15&e]},t.prototype.toString=t.prototype.hex,t.prototype.digest=function(){this.finalize();var t=this.h0,h=this.h1,s=this.h2,i=this.h3,e=this.h4;return[t>>24&255,t>>16&255,t>>8&255,255&t,h>>24&255,h>>16&255,h>>8&255,255&h,s>>24&255,s>>16&255,s>>8&255,255&s,i>>24&255,i>>16&255,i>>8&255,255&i,e>>24&255,e>>16&255,e>>8&255,255&e]},t.prototype.array=t.prototype.digest,t.prototype.arrayBuffer=function(){this.finalize();var t=new ArrayBuffer(20),h=new DataView(t);return h.setUint32(0,this.h0),h.setUint32(4,this.h1),h.setUint32(8,this.h2),h.setUint32(12,this.h3),h.setUint32(16,this.h4),t};var y=c();i?module.exports=y:(h.sha1=y,e&&define(function(){return y}))}(); -------------------------------------------------------------------------------- /src/web/assets/libs/safari/arrayBuffer.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | File.prototype.arrayBuffer = File.prototype.arrayBuffer || myArrayBuffer; 3 | Blob.prototype.arrayBuffer = Blob.prototype.arrayBuffer || myArrayBuffer; 4 | 5 | function myArrayBuffer() { 6 | // this: File or Blob 7 | return new Promise((resolve) => { 8 | let fr = new FileReader(); 9 | fr.onload = () => { 10 | resolve(fr.result); 11 | }; 12 | fr.readAsArrayBuffer(this); 13 | }) 14 | } 15 | })(); 16 | -------------------------------------------------------------------------------- /src/web/controllers/FileController.go: -------------------------------------------------------------------------------- 1 | package controllers 2 | 3 | import ( 4 | "fmt" 5 | "github.com/kataras/iris/v12" 6 | "github.com/kataras/iris/v12/sessions" 7 | "gocloud/common" 8 | "gocloud/datamodels" 9 | "gocloud/services" 10 | "io" 11 | "net/http" 12 | "os" 13 | "strconv" 14 | "strings" 15 | "time" 16 | ) 17 | 18 | type FileController struct { 19 | Ctx iris.Context 20 | Service services.IFileService 21 | } 22 | 23 | func (this *FileController) PostUpload() { 24 | sess := sessions.Get(this.Ctx) 25 | 26 | user, ok := (sess.Get("user")).(*datamodels.UserModel) 27 | if !ok { 28 | this.Ctx.Application().Logger().Error("parse user err by sesssion") 29 | } 30 | 31 | //recv file info 32 | 33 | qetag := this.Ctx.Request().FormValue("qetag") 34 | flowIdentifier := this.Ctx.Request().FormValue("flowIdentifier") 35 | flowTotalChunks := this.Ctx.Request().FormValue("flowTotalChunks") 36 | flowChunkNumber := this.Ctx.Request().FormValue("flowChunkNumber") 37 | 38 | if len(qetag) == 0 || len(flowIdentifier) == 0 || len(flowTotalChunks) == 0 || len(flowChunkNumber) == 0 { 39 | this.Ctx.JSON(datamodels.RespModel{Status: 0, Msg: fmt.Sprintf("the upload info error")}) 40 | return 41 | } 42 | 43 | //recv file stream 44 | this.Ctx.Request().ParseForm() 45 | file, fileHeader, err := this.Ctx.FormFile("file") 46 | if err != nil { 47 | this.Ctx.JSON(datamodels.RespModel{Status: 0, Msg: fmt.Sprintf("Failed to get data,err:%s\n", err.Error())}) 48 | return 49 | } 50 | defer file.Close() 51 | 52 | fileMeta := datamodels.FileModel{ 53 | FileName: fileHeader.Filename, 54 | Location: common.Local_Storage_Mount + user.Username + "/" + qetag + "-[" + flowIdentifier + "]-" + flowTotalChunks + "-" + flowChunkNumber, 55 | UploadAt: time.Now().Format("2006-01-02 15:04:05"), 56 | } 57 | 58 | if !common.Exists(common.Local_Storage_Mount + user.Username + "/") { 59 | err := os.MkdirAll(common.Local_Storage_Mount+user.Username+"/", os.ModePerm) 60 | if err != nil { 61 | panic(err) 62 | } 63 | } 64 | 65 | newFile, err := os.Create(fileMeta.Location) 66 | if err != nil { 67 | this.Ctx.JSON(datamodels.RespModel{Status: 0, Msg: fmt.Sprintf("Failed to create file,err:%s\n", err.Error())}) 68 | return 69 | } 70 | defer newFile.Close() 71 | fileMeta.FileSize, err = io.Copy(newFile, file) 72 | if err != nil { 73 | fmt.Printf("Failed to save data into file,err:%s\n", err.Error()) 74 | return 75 | } 76 | 77 | } 78 | 79 | func (this *FileController) PostUploadfinshed() { 80 | sess := sessions.Get(this.Ctx) 81 | 82 | user, ok := (sess.Get("user")).(*datamodels.UserModel) 83 | if !ok { 84 | this.Ctx.Application().Logger().Error("parse user err by sesssion") 85 | } 86 | 87 | qetag := this.Ctx.FormValue("qetag") 88 | flowIdentifier := this.Ctx.FormValue("flowIdentifier") 89 | flowTotalChunks := this.Ctx.FormValue("flowTotalChunks") 90 | fileExt := this.Ctx.FormValue("fileExt") 91 | parent_dir, _ := strconv.ParseInt(this.Ctx.FormValue("parentDir"), 10, 0) 92 | fileSize, _ := strconv.ParseInt(this.Ctx.Request().FormValue("fileSize"), 10, 0) 93 | fileName := this.Ctx.Request().FormValue("fileName") 94 | 95 | if !common.Exists(common.Local_Storage_Mount + user.Username + "/" + qetag + "-[" + flowIdentifier + "]-" + flowTotalChunks + "-" + "1") { 96 | this.Ctx.JSON(datamodels.RespModel{Status: 0, Msg: "file not exist"}) 97 | return 98 | } 99 | newFileName := common.Local_Storage_Mount + user.Username + "/" + qetag + "-[" + flowIdentifier + "]." + fileExt 100 | newFile, err := os.Create(newFileName) 101 | if err != nil { 102 | this.Ctx.JSON(datamodels.RespModel{Status: 0, Msg: fmt.Sprintf("Failed to create file,err:%s\n", err.Error())}) 103 | return 104 | } 105 | 106 | filesCount, err := strconv.ParseInt(flowTotalChunks, 10, 0) 107 | if err != nil { 108 | panic(err) 109 | } 110 | for i := 1; i <= int(filesCount); i++ { 111 | oldFilePath := common.Local_Storage_Mount + user.Username + "/" + qetag + "-[" + flowIdentifier + "]-" + flowTotalChunks + "-" + strconv.Itoa(i) 112 | oldfile, err := os.Open(oldFilePath) 113 | if err != nil { 114 | _ = newFile.Close() 115 | _, _ = this.Ctx.JSON(datamodels.RespModel{Status: 0, Msg: fmt.Sprintf("Failed to open file,err:%s\n", err.Error())}) 116 | return 117 | } 118 | 119 | _, err = io.Copy(newFile, oldfile) 120 | err = os.Remove(oldFilePath) 121 | if err != nil { 122 | _ = oldfile.Close() 123 | _ = newFile.Close() 124 | this.Ctx.Application().Logger().Error("delete temp file error:" + err.Error()) 125 | return 126 | } 127 | _ = oldfile.Close() 128 | } 129 | _ = newFile.Close() 130 | 131 | // calc the file qetag 132 | qetagBK, err := common.GetEtag(newFileName) 133 | if err != nil { 134 | this.Ctx.JSON(datamodels.RespModel{ 135 | Status: 0, 136 | Msg: err.Error(), 137 | }) 138 | return 139 | } 140 | if qetagBK != qetag { 141 | this.Ctx.JSON(datamodels.RespModel{ 142 | Status: 0, 143 | Msg: "qetag inconsistent", 144 | }) 145 | return 146 | } 147 | 148 | // save file info to db 149 | succ, err := this.Service.AddFile(qetag, fileName, fileSize, newFileName) 150 | if !succ { 151 | this.Ctx.JSON(datamodels.RespModel{ 152 | Status: 0, 153 | Msg: err.Error(), 154 | }) 155 | return 156 | } 157 | 158 | succ, err = this.Service.AddUserFileRelation(user.Username, qetag, fileName, newFileName, fileSize, 0, parent_dir) 159 | if !succ { 160 | this.Ctx.JSON(datamodels.RespModel{ 161 | Status: 0, 162 | Msg: err.Error(), 163 | }) 164 | return 165 | } 166 | 167 | this.Ctx.JSON(datamodels.RespModel{ 168 | Status: 1, 169 | Msg: "upload success", 170 | }) 171 | } 172 | 173 | func (this *FileController) GetUserindexfiles() { 174 | sess := sessions.Get(this.Ctx) 175 | user, ok := (sess.Get("user")).(*datamodels.UserModel) 176 | if !ok { 177 | this.Ctx.Application().Logger().Error("parse user err by sesssion") 178 | return 179 | } 180 | 181 | parents, _ := strconv.ParseInt(this.Ctx.Request().FormValue("p"), 10, 0) 182 | 183 | files, err := this.Service.QueryUserFils(user.Username, parents,1) 184 | if err != nil { 185 | this.Ctx.JSON(datamodels.RespModel{ 186 | Status: 0, 187 | Msg: "get user index files error", 188 | }) 189 | return 190 | } 191 | this.Ctx.JSON(datamodels.RespModel{ 192 | Status: 1, 193 | Msg: "OK", 194 | Data: files, 195 | }) 196 | } 197 | 198 | func (this *FileController) GetDownloadfileBy(filename string) { 199 | 200 | fileqetag := this.Ctx.Request().FormValue("fileqetag") 201 | if len(strings.TrimSpace(fileqetag)) == 0 { 202 | this.Ctx.JSON(datamodels.RespModel{ 203 | Status: 0, 204 | Msg: "upload error:invalid fileqetag", 205 | }) 206 | return 207 | } 208 | 209 | file, err := this.Service.GetFileMeta(fileqetag) 210 | if err != nil { 211 | this.Ctx.JSON(datamodels.RespModel{ 212 | Status: 0, 213 | Msg: "upload error:" + err.Error(), 214 | }) 215 | return 216 | } 217 | 218 | //download 219 | f, err := os.Open(file.Location) 220 | if err != nil { 221 | this.Ctx.JSON(datamodels.RespModel{ 222 | Status: 0, 223 | Msg: "open file error:" + err.Error(), 224 | }) 225 | return 226 | } 227 | defer f.Close() 228 | _t, err := time.Parse("2006-01-02 15:04:05", file.UploadAt) 229 | if err != nil { 230 | this.Ctx.JSON(datamodels.RespModel{ 231 | Status: 0, 232 | Msg: "parse time error:" + err.Error(), 233 | }) 234 | return 235 | } 236 | this.Ctx.ResponseWriter().Header().Set("Content-Disposition","attachment; filename=\""+filename+"\"") 237 | 238 | http.ServeContent(this.Ctx.ResponseWriter(), this.Ctx.Request(), "", _t, f) 239 | //this.Ctx.SendFile(file.Location,file.FileName) 240 | } 241 | 242 | func (this *FileController) GetCreatedirBy(parent_dir int,dir_name string) { 243 | 244 | sess := sessions.Get(this.Ctx) 245 | 246 | user, ok := (sess.Get("user")).(*datamodels.UserModel) 247 | if !ok { 248 | this.Ctx.Application().Logger().Error("parse user err by sesssion") 249 | return 250 | } 251 | 252 | // check the 253 | 254 | // it is a simple method to generate a 'dir sha1', 255 | // but I think it's enough 256 | // because the dir_name is unique 257 | dir_sha1 :=common.Sha1([]byte(user.Username+dir_name+strconv.Itoa(parent_dir))) 258 | succ, err := this.Service.AddUserFileRelation(user.Username, dir_sha1, dir_name, "", 0, 1,int64(parent_dir)) 259 | if !succ { 260 | this.Ctx.JSON(datamodels.RespModel{ 261 | Status: 0, 262 | Msg: err.Error(), 263 | }) 264 | return 265 | } 266 | this.Ctx.JSON(datamodels.RespModel{ 267 | Status: 1, 268 | Msg: "OK", 269 | }) 270 | } 271 | 272 | func (this *FileController) GetDeleteBy(qetag string,parent_id int64) { 273 | sess := sessions.Get(this.Ctx) 274 | 275 | user, ok := (sess.Get("user")).(*datamodels.UserModel) 276 | if !ok { 277 | this.Ctx.Application().Logger().Error("parse user err by sesssion") 278 | return 279 | } 280 | succ,err:=this.Service.DeleteFile(user.Username,qetag,parent_id) 281 | if err !=nil||!succ{ 282 | this.Ctx.JSON(datamodels.RespModel{ 283 | Status: 0, 284 | Msg: err.Error(), 285 | }) 286 | return 287 | } 288 | this.Ctx.JSON(datamodels.RespModel{ 289 | Status: 1, 290 | Msg: "OK", 291 | }) 292 | } 293 | 294 | func (this *FileController) PostFilesecondspass() { 295 | sess := sessions.Get(this.Ctx) 296 | 297 | user, ok := (sess.Get("user")).(*datamodels.UserModel) 298 | if !ok { 299 | this.Ctx.Application().Logger().Error("parse user err by sesssion") 300 | } 301 | 302 | qetag := this.Ctx.FormValue("qetag") 303 | parent_dir, _ := strconv.ParseInt(this.Ctx.FormValue("parentDir"), 10, 0) 304 | fileName := this.Ctx.Request().FormValue("fileName") 305 | 306 | meta,err :=this.Service.GetFileMeta(qetag) 307 | if err!=nil{ 308 | this.Ctx.JSON(datamodels.RespModel{ 309 | Status: 0, 310 | Msg: err.Error(), 311 | }) 312 | return 313 | } 314 | 315 | if meta==nil{ 316 | //don't hava same file,need upload file 317 | this.Ctx.JSON(datamodels.RespModel{ 318 | Status: 0, 319 | Msg: "don't hava same file", 320 | }) 321 | return 322 | } 323 | 324 | succ,err :=this.Service.AddUserFileRelation(user.Username,qetag,fileName,meta.Location,meta.FileSize,0,parent_dir) 325 | if err!=nil || !succ{ 326 | this.Ctx.JSON(datamodels.RespModel{ 327 | Status: 0, 328 | Msg: err.Error(), 329 | }) 330 | return 331 | } 332 | this.Ctx.JSON(datamodels.RespModel{ 333 | Status: 1, 334 | Msg: "OK", 335 | }) 336 | 337 | 338 | } 339 | 340 | func (this *FileController) GetRenamefileBy(id int64, name string) { 341 | if id<=0{ 342 | this.Ctx.JSON(datamodels.RespModel{ 343 | Status: 0, 344 | Msg: "the id error", 345 | }) 346 | return 347 | } 348 | 349 | succ,err := this.Service.UpdateUserFileName(id,name) 350 | if err!=nil ||!succ{ 351 | this.Ctx.JSON(datamodels.RespModel{ 352 | Status: 0, 353 | Msg: err.Error(), 354 | }) 355 | return 356 | } 357 | this.Ctx.JSON(datamodels.RespModel{ 358 | Status: 1, 359 | Msg: "OK", 360 | }) 361 | 362 | } 363 | 364 | func (this *FileController) GetUserdirsBy(id int) { 365 | sess := sessions.Get(this.Ctx) 366 | 367 | user, ok := (sess.Get("user")).(*datamodels.UserModel) 368 | if !ok { 369 | this.Ctx.Application().Logger().Error("parse user err by sesssion") 370 | return 371 | } 372 | dirs,err:=this.Service.GetUserDirByUser(user.Username,id) 373 | if err!=nil{ 374 | this.Ctx.JSON(datamodels.RespModel{ 375 | Status: 0, 376 | Msg: err.Error(), 377 | }) 378 | return 379 | } 380 | this.Ctx.JSON(datamodels.RespModel{ 381 | Status: 1, 382 | Msg: "OK", 383 | Data: dirs, 384 | }) 385 | 386 | } 387 | 388 | func (this *FileController) PostMovefile() { 389 | user_file_id,err :=strconv.ParseInt( this.Ctx.Request().FormValue("id"),10,0) 390 | dir ,err:=strconv.ParseInt( this.Ctx.Request().FormValue("dir"),10,0) 391 | 392 | if err!=nil{ 393 | this.Ctx.JSON(datamodels.RespModel{ 394 | Status: 0, 395 | Msg: err.Error(), 396 | }) 397 | return 398 | } 399 | 400 | succ,err := this.Service.MoveFileTo(user_file_id,dir) 401 | if !succ||err!=nil{ 402 | this.Ctx.JSON(datamodels.RespModel{ 403 | Status: 0, 404 | Msg: err.Error(), 405 | }) 406 | return 407 | } 408 | 409 | this.Ctx.JSON(datamodels.RespModel{ 410 | Status: 1, 411 | Msg: "OK", 412 | }) 413 | 414 | 415 | 416 | } 417 | 418 | func (this *FileController) GetUsersharefilesBy(user_name string){ 419 | if len(user_name)==0{ 420 | this.Ctx.JSON(datamodels.RespModel{ 421 | Status: 0, 422 | Msg: "Invalid user name", 423 | }) 424 | return 425 | } 426 | 427 | files,err := this.Service.QueryUserShareFiles(user_name) 428 | 429 | if err!=nil{ 430 | this.Ctx.JSON(datamodels.RespModel{ 431 | Status: 0, 432 | Msg: err.Error(), 433 | }) 434 | return 435 | } 436 | 437 | 438 | this.Ctx.JSON(datamodels.RespModel{ 439 | Status: 1, 440 | Msg: "OK", 441 | Data: files, 442 | }) 443 | } 444 | 445 | func (this *FileController) GetUserrecyclefiles(){ 446 | sess := sessions.Get(this.Ctx) 447 | 448 | user, ok := (sess.Get("user")).(*datamodels.UserModel) 449 | if !ok { 450 | this.Ctx.Application().Logger().Error("parse user err by sesssion") 451 | return 452 | } 453 | 454 | userFiles,err:=this.Service.QueryUserFilsByStatus(user.Username,3) 455 | if err != nil { 456 | this.Ctx.JSON(datamodels.RespModel{ 457 | Status: 0, 458 | Msg: err.Error(), 459 | }) 460 | return 461 | } 462 | this.Ctx.JSON(datamodels.RespModel{ 463 | Status: 1, 464 | Msg: "OK", 465 | Data: userFiles, 466 | }) 467 | 468 | } -------------------------------------------------------------------------------- /src/web/controllers/IndexController.go: -------------------------------------------------------------------------------- 1 | package controllers 2 | 3 | import ( 4 | "github.com/kataras/iris/v12" 5 | "github.com/kataras/iris/v12/mvc" 6 | "github.com/kataras/iris/v12/sessions" 7 | "gocloud/datamodels" 8 | "gocloud/services" 9 | ) 10 | 11 | type IndexController struct { 12 | Ctx iris.Context 13 | IndexService services.IIndexService 14 | } 15 | 16 | func (this *IndexController) Get() mvc.View{ 17 | sess:=sessions.Get(this.Ctx) 18 | //if auth,err:=sess.GetBoolean("authenticated");!auth||err!=nil{ 19 | // return mvc.View{ 20 | // Layout: "shared/layout.fw.html", 21 | // Name: "login/login.html", 22 | // } 23 | //} 24 | 25 | user ,ok:=(sess.Get("user")).(*datamodels.UserModel) 26 | if !ok{ 27 | this.Ctx.Application().Logger().Error("get user err by sesssion") 28 | } 29 | 30 | return mvc.View{ 31 | Name: "index/index.html", 32 | Data: iris.Map{ 33 | "username":user.Username, 34 | }, 35 | } 36 | } 37 | 38 | 39 | -------------------------------------------------------------------------------- /src/web/controllers/LoginController.go: -------------------------------------------------------------------------------- 1 | package controllers 2 | 3 | import ( 4 | "github.com/kataras/iris/v12" 5 | "github.com/kataras/iris/v12/mvc" 6 | "github.com/kataras/iris/v12/sessions" 7 | "gocloud/common" 8 | "gocloud/datamodels" 9 | "gocloud/services" 10 | "strings" 11 | ) 12 | 13 | type LoginController struct { 14 | Ctx iris.Context 15 | Service services.ILoginService 16 | 17 | } 18 | 19 | func (this *LoginController) Get() mvc.View { 20 | sess:=sessions.Get(this.Ctx) 21 | if auth,err:=sess.GetBoolean("authenticated");auth&&err==nil{ 22 | return mvc.View{ 23 | Name: "index/index.html", 24 | } 25 | } 26 | return mvc.View{ 27 | Layout:"shared/layout.fw.html", 28 | Name:"login/login.html", 29 | } 30 | } 31 | 32 | // PostSignin the user signin 33 | func (this *LoginController) PostSignin() { 34 | data :=&datamodels.UserModel{} 35 | 36 | this.Ctx.ReadJSON(data) 37 | 38 | if len(strings.TrimSpace(data.Email))==0{ 39 | this.Ctx.JSON(datamodels.RespModel{ 40 | Status: 0, 41 | Msg: "Invalid Email", 42 | }) 43 | return 44 | } 45 | 46 | if len(strings.TrimSpace(data.Userpwd))==0{ 47 | this.Ctx.JSON(datamodels.RespModel{ 48 | Status: 0, 49 | Msg: "Invalid Password", 50 | }) 51 | 52 | return 53 | } 54 | 55 | 56 | user,err :=this.Service.Signin(data) 57 | if err!=nil{ 58 | this.Ctx.Application().Logger().Error(err) 59 | this.Ctx.JSON("system error") 60 | return 61 | } 62 | if user.Userpwd != common.Sha1([]byte(data.Userpwd+common.User_Pwd_Sha1_Salt)){ 63 | this.Ctx.JSON(datamodels.RespModel{ 64 | Status: 0, 65 | Msg: "The Email or Password Error", 66 | }) 67 | return 68 | } 69 | 70 | sessions.Get(this.Ctx).Set("authenticated",true) 71 | sessions.Get(this.Ctx).Set("user",user) 72 | this.Ctx.JSON(datamodels.RespModel{ 73 | Status: 0, 74 | Msg: "OK", 75 | }) 76 | } 77 | 78 | // PostSignup the user singUp 79 | func (this *LoginController) PostSignup() { 80 | data :=&datamodels.UserModel{} 81 | 82 | this.Ctx.ReadJSON(data) 83 | 84 | _,err :=this.Service.Signup(data) 85 | if err!=nil{ 86 | this.Ctx.Application().Logger().Error(err) 87 | this.Ctx.JSON("system error") 88 | return 89 | } 90 | this.Ctx.JSON("OK") 91 | } 92 | 93 | // GetLogout the user loguout 94 | func (this *LoginController) GetLogout() { 95 | sessions.Get(this.Ctx).Set("authenticated",false) 96 | sessions.Get(this.Ctx).Clear() 97 | this.Ctx.JSON(datamodels.RespModel{ 98 | Status: 0, 99 | Msg: "OK", 100 | }) 101 | } 102 | -------------------------------------------------------------------------------- /src/web/controllers/ShareController.go: -------------------------------------------------------------------------------- 1 | package controllers 2 | 3 | import ( 4 | "github.com/kataras/iris/v12" 5 | "github.com/kataras/iris/v12/mvc" 6 | "github.com/kataras/iris/v12/sessions" 7 | "gocloud/datamodels" 8 | "gocloud/services" 9 | "gocloud/web/middleware" 10 | "net/http" 11 | "os" 12 | "strconv" 13 | "strings" 14 | "time" 15 | ) 16 | 17 | type ShareController struct { 18 | Ctx iris.Context 19 | Service services.IFileService 20 | } 21 | 22 | func (this *ShareController)PostCreateshare() { 23 | this.Ctx.Proceed (middleware.NewAuth()) 24 | if this.Ctx.IsStopped(){ 25 | return 26 | } 27 | 28 | user_file_id,_ :=strconv.ParseInt( this.Ctx.Request().FormValue("user_file_id"),10,0) 29 | 30 | share_pwd :=this.Ctx.Request().FormValue("share_pwd") 31 | share_time ,_:=strconv.ParseInt( this.Ctx.Request().FormValue("share_time"),10,0) 32 | 33 | if user_file_id==0{ 34 | this.Ctx.JSON(datamodels.RespModel{ 35 | Status: 0, 36 | Msg: "share error:invalid user_file_id", 37 | }) 38 | return 39 | } 40 | 41 | sharefile,err:=this.Service.QueryShareFileByUserFileId(user_file_id) 42 | if err!=nil{ 43 | this.Ctx.JSON(datamodels.RespModel{ 44 | Status: 0, 45 | Msg: err.Error(), 46 | }) 47 | return 48 | } 49 | 50 | if sharefile!=nil{ 51 | this.Ctx.JSON(datamodels.RespModel{ 52 | Status: 0, 53 | Msg: "this file has been shared", 54 | }) 55 | return 56 | } 57 | 58 | 59 | 60 | link,succ,err:=this.Service.CreateShareFile(user_file_id,share_time,share_pwd) 61 | if !succ||err!=nil{ 62 | this.Ctx.JSON(datamodels.RespModel{ 63 | Status: 0, 64 | Msg: err.Error(), 65 | }) 66 | return 67 | } 68 | 69 | 70 | this.Ctx.JSON(datamodels.RespModel{ 71 | Status: 1, 72 | Msg: "", 73 | Data: iris.Map{ 74 | "link":link, 75 | }, 76 | }) 77 | 78 | } 79 | 80 | func (this *ShareController) Get() mvc.View { 81 | return mvc.View{ 82 | Layout:"shared/layout.fw.html", 83 | Name:"error/error.html", 84 | } 85 | } 86 | 87 | func (this *ShareController) GetBy(share_id string) mvc.View { 88 | sess := sessions.Get(this.Ctx) 89 | 90 | user, ok := (sess.Get("user")).(*datamodels.UserModel) 91 | username:="" 92 | if !ok { 93 | username="" 94 | }else { 95 | username = user.Username 96 | } 97 | 98 | return mvc.View{ 99 | Name:"share/share.html", 100 | Data: iris.Map{ 101 | "share_id":share_id, 102 | "username":username, 103 | }, 104 | } 105 | 106 | } 107 | 108 | func (this *ShareController) GetFileBy(share_id string) { 109 | if len(share_id)==0||share_id=="undefined"{ 110 | this.Ctx.JSON(datamodels.RespModel{ 111 | Status: 0, 112 | Msg: "invalid share_id", 113 | }) 114 | return 115 | } 116 | 117 | share, err:=this.Service.QueryShareFileBy(share_id) 118 | if err !=nil{ 119 | this.Ctx.JSON(datamodels.RespModel{ 120 | Status: 0, 121 | Msg: err.Error(), 122 | }) 123 | return 124 | } 125 | 126 | if share == nil{ 127 | //file not exist 128 | this.Ctx.JSON(datamodels.RespModel{ 129 | Status: 0, 130 | Msg: "", 131 | }) 132 | return 133 | } 134 | 135 | if len(share.SharePwd)==0{ 136 | //public share 137 | this.Ctx.JSON(datamodels.RespModel{ 138 | Status: 1, 139 | Msg: "OK", 140 | Data: iris.Map{ 141 | "pwd":0, 142 | }, 143 | }) 144 | return 145 | } 146 | //password share 147 | this.Ctx.JSON(datamodels.RespModel{ 148 | Status: 1, 149 | Msg: "OK", 150 | Data: iris.Map{ 151 | "pwd":1, 152 | }, 153 | }) 154 | } 155 | 156 | func (this *ShareController) PostValid() { 157 | share_id:=this.Ctx.Request().FormValue("share_id") 158 | pwd:=this.Ctx.Request().FormValue("pwd") 159 | if len(share_id)==0{ 160 | this.Ctx.JSON(datamodels.RespModel{ 161 | Status: 0, 162 | Msg: "Invalid share id", 163 | }) 164 | return 165 | } 166 | 167 | usershare,err :=this.Service.QueryUserShareFileBy(share_id) 168 | if err!=nil{ 169 | this.Ctx.JSON(datamodels.RespModel{ 170 | Status: 0, 171 | Msg: err.Error(), 172 | }) 173 | return 174 | } 175 | 176 | if usershare.SharePwd !=pwd{ 177 | this.Ctx.JSON(datamodels.RespModel{ 178 | Status: 0, 179 | Msg: "the share passwprd invalid", 180 | }) 181 | return 182 | } 183 | usershare.SharePwd ="" 184 | this.Ctx.JSON(datamodels.RespModel{ 185 | Status: 1, 186 | Msg: "OK", 187 | Data: usershare, 188 | }) 189 | } 190 | 191 | func (this *ShareController) GetDownloadfileBy(filename string) { 192 | 193 | share_id :=this.Ctx.Request().FormValue("share_id") 194 | share_pwd:=this.Ctx.Request().FormValue("share_pwd") 195 | if len(strings.TrimSpace(share_id)) == 0 ||len(strings.TrimSpace(share_pwd))==0{ 196 | this.Ctx.JSON(datamodels.RespModel{ 197 | Status: 0, 198 | Msg: "upload error:invalid share_id/share_pwd", 199 | }) 200 | return 201 | } 202 | _,err:=this.Service.QueryShareFileAndValid(share_id,share_pwd) 203 | if err!=nil{ 204 | this.Ctx.JSON(datamodels.RespModel{ 205 | Status: 0, 206 | Msg: "upload error:" + err.Error(), 207 | }) 208 | return 209 | } 210 | 211 | share,err:=this.Service.QueryUserShareFileBy(share_id) 212 | if err!=nil{ 213 | this.Ctx.JSON(datamodels.RespModel{ 214 | Status: 0, 215 | Msg: "upload error:" + err.Error(), 216 | }) 217 | return 218 | } 219 | 220 | 221 | 222 | file, err := this.Service.GetFileMeta(share.FileQetag) 223 | if err != nil { 224 | this.Ctx.JSON(datamodels.RespModel{ 225 | Status: 0, 226 | Msg: "upload error:" + err.Error(), 227 | }) 228 | return 229 | } 230 | 231 | //download 232 | f, err := os.Open(file.Location) 233 | if err != nil { 234 | this.Ctx.JSON(datamodels.RespModel{ 235 | Status: 0, 236 | Msg: "open file error:" + err.Error(), 237 | }) 238 | return 239 | } 240 | defer f.Close() 241 | _t, err := time.Parse("2006-01-02 15:04:05", file.UploadAt) 242 | if err != nil { 243 | this.Ctx.JSON(datamodels.RespModel{ 244 | Status: 0, 245 | Msg: "parse time error:" + err.Error(), 246 | }) 247 | return 248 | } 249 | this.Ctx.ResponseWriter().Header().Set("Content-Disposition","attachment; filename=\""+filename+"\"") 250 | 251 | http.ServeContent(this.Ctx.ResponseWriter(), this.Ctx.Request(), "", _t, f) 252 | } 253 | 254 | func (this *ShareController) PostSavefile() { 255 | 256 | this.Ctx.Proceed (middleware.NewAuth()) 257 | if this.Ctx.IsStopped(){ 258 | return 259 | } 260 | 261 | 262 | sess := sessions.Get(this.Ctx) 263 | 264 | user, ok := (sess.Get("user")).(*datamodels.UserModel) 265 | if !ok { 266 | this.Ctx.Application().Logger().Error("parse user err by sesssion") 267 | return 268 | } 269 | 270 | share_id :=this.Ctx.Request().FormValue("share_id") 271 | share_pwd:=this.Ctx.Request().FormValue("share_pwd") 272 | dir ,_:=strconv.ParseInt( this.Ctx.Request().FormValue("dir"),10,0) 273 | 274 | if len(strings.TrimSpace(share_id)) == 0 ||len(strings.TrimSpace(share_pwd))==0{ 275 | this.Ctx.JSON(datamodels.RespModel{ 276 | Status: 0, 277 | Msg: "upload error:invalid share_id/share_pwd", 278 | }) 279 | return 280 | } 281 | fileshare,err:=this.Service.QueryShareFileAndValid(share_id,share_pwd) 282 | if err!=nil{ 283 | this.Ctx.JSON(datamodels.RespModel{ 284 | Status: 0, 285 | Msg: "error:" + err.Error(), 286 | }) 287 | return 288 | } 289 | 290 | userfile,err:=this.Service.GetUserFileByID(int64(fileshare.UserFileId)) 291 | succ,err :=this.Service.AddUserFileRelation(user.Username,userfile.FileQetag,userfile.FileName,"",userfile.FileSize,userfile.IsDir, dir) 292 | if !succ||err!=nil{ 293 | this.Ctx.JSON(datamodels.RespModel{ 294 | Status: 0, 295 | Msg: err.Error(), 296 | }) 297 | } 298 | 299 | this.Ctx.JSON(datamodels.RespModel{ 300 | Status: 1, 301 | Msg: "OK", 302 | }) 303 | } 304 | 305 | 306 | func (this *ShareController) GetCancelshareBy(share_id string) { 307 | this.Ctx.Proceed (middleware.NewAuth()) 308 | if this.Ctx.IsStopped(){ 309 | return 310 | } 311 | //idInt, _:= strconv.ParseInt(id,10,0) 312 | 313 | succ,err:=this.Service.CancelShareFile(share_id) 314 | 315 | if !succ || err!=nil{ 316 | this.Ctx.JSON(datamodels.RespModel{ 317 | Status: 0, 318 | Msg: err.Error(), 319 | }) 320 | return 321 | } 322 | 323 | this.Ctx.JSON(datamodels.RespModel{ 324 | Status: 1, 325 | Msg: "OK", 326 | }) 327 | 328 | } 329 | 330 | -------------------------------------------------------------------------------- /src/web/middleware/auth_middleware.go: -------------------------------------------------------------------------------- 1 | package middleware 2 | 3 | import ( 4 | "github.com/kataras/iris/v12/context" 5 | "github.com/kataras/iris/v12/sessions" 6 | ) 7 | 8 | func NewAuth() context.Handler{ 9 | u := &auth{} 10 | return u.auth 11 | } 12 | 13 | type auth struct { 14 | 15 | } 16 | func (this * auth) auth(ctx context.Context) { 17 | sess:=sessions.Get(ctx) 18 | if auth,err:=sess.GetBoolean("authenticated");!auth||err!=nil{ 19 | if ctx.IsAjax(){ 20 | ctx.StatusCode(401) 21 | ctx.StopExecution() 22 | return 23 | } 24 | ctx.Redirect("/login") 25 | return 26 | } 27 | ctx.Next() 28 | } -------------------------------------------------------------------------------- /src/web/views/error/error.html: -------------------------------------------------------------------------------- 1 |

error

2 | 3 | -------------------------------------------------------------------------------- /src/web/views/index/index.html: -------------------------------------------------------------------------------- 1 |
2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | {{.username}} 23 | 24 | 25 | 个人中心 26 | 退 出 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 全部文件 43 | 44 | 45 | 46 | 47 | 我的分享 48 | 49 | 50 | 51 | 52 | 回收站 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 上传 67 | 新建文件夹 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | $item.FileName$ 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 120 | 121 | 122 | 138 | 139 | 140 | 141 | 142 | 143 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 提示: 请注意,回收站内的文件会在30天后被彻底删除。 168 | 169 | 170 | 171 | 172 | 175 | 176 | 177 | 178 | 179 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 上传列表 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 239 | 240 | 241 | 242 | 249 | 250 | 251 | 252 | 253 | 254 | 255 | 256 | 257 | 258 | 259 | 260 | 264 | 265 | 266 | 267 | 268 | 269 | 270 | 274 | 275 | 276 | 279 | 确认删除? 280 | 281 | 取 消 282 | 确 定 283 | 284 | 285 | 286 | 287 | 288 | 289 | 290 | 291 | 298 | 299 | $node.label$ 300 | 301 | 302 | 303 | 304 | 305 | 306 | 310 | 311 | 312 | 313 | 314 | 315 | 316 | 317 | 318 | 319 | 320 | 321 | 322 | 323 | 324 | 325 | 326 | 327 | 328 | 329 | 330 | 331 | 332 | 333 | 334 | 335 | 336 | 337 | 338 | 339 | 340 | 341 | 349 | 350 | 351 | 366 | 367 | 368 |
369 | 370 | 371 | 372 | 373 | 374 | -------------------------------------------------------------------------------- /src/web/views/login/login.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | GoPan 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 |
17 | 27 | 38 |
39 |
40 |
41 |

已有帐号?

42 |

请使用您的帐号进行登录

43 | 44 |
45 |
46 |

没有帐号?

47 |

立即注册加入我们,和我们一起开始旅程吧

48 | 49 |
50 |
51 |
52 |
53 | 54 |
55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | -------------------------------------------------------------------------------- /src/web/views/share/share.html: -------------------------------------------------------------------------------- 1 |
2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | {{.username}} 21 | 22 | 23 | 个人中心 24 | 退 出 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 |

39 | 文件不存在或分享链接已过期 40 |
41 |
42 | 43 | 44 | 45 | 46 | 47 | 48 |
50 | 51 | 52 | 53 | 54 | 立即打开 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 |
63 | 64 | 65 | 66 |
67 |
68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 |
76 | 77 |
78 | 79 | 80 | 81 | 82 | $file_name$ 83 | 84 | 85 | 文件保存 86 | 文件下载 87 | 88 | 89 | 90 | 91 | 92 | $create_time$ 93 | 有效期: $share_time$ 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 |
103 | 104 | 105 |
106 | $file_name$ 107 |
108 | 109 | 110 |
111 | 112 |
113 | 114 | 115 |
116 | 117 | 118 | 119 |
120 | 121 | 122 |
123 | 124 | 125 |
126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 140 | 141 | $node.label$ 142 | 143 | 144 | 145 | 146 | 147 | 148 | 152 | 153 | 154 |
155 | 156 | 157 | 160 | 161 | -------------------------------------------------------------------------------- /src/web/views/shared/layout.fw.html: -------------------------------------------------------------------------------- 1 | 2 | {{yield}} 3 | -------------------------------------------------------------------------------- /src/web/views/shared/layout.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | GoPan 6 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 220 | 221 | 222 | 223 | 224 | {{yield}} 225 | 226 | 227 | 228 | 229 | 232 | -------------------------------------------------------------------------------- /start.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | set -e 3 | docker-compose up -d --build --force-recreate --------------------------------------------------------------------------------