mirror of
https://github.com/eiblog/eiblog.git
synced 2026-02-08 23:52:26 +08:00
Compare commits
60 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fb66b6871e | ||
|
|
5ae76f243e | ||
|
|
051b034e51 | ||
|
|
27439ecc71 | ||
|
|
d02c838447 | ||
|
|
d17acf5325 | ||
|
|
b278ca377f | ||
|
|
93131441e4 | ||
|
|
ddcc6c2d2e | ||
|
|
ef63ae9598 | ||
|
|
2ed9db5c7b | ||
|
|
06a12bc6f9 | ||
|
|
6524b45751 | ||
|
|
ceb9e2690b | ||
|
|
405fbaf24f | ||
|
|
3245c0e0d3 | ||
|
|
badc62e3f0 | ||
|
|
a5561f257b | ||
|
|
eb37b83ebd | ||
|
|
b2fab703fc | ||
|
|
37deb390d9 | ||
|
|
6fa5088352 | ||
|
|
e023a33786 | ||
|
|
6f818c4b5d | ||
|
|
9ad22fb2d9 | ||
|
|
fc37d5e093 | ||
|
|
61024bfebd | ||
|
|
f20c4a6063 | ||
|
|
c24e6bf7bd | ||
|
|
ade94168d3 | ||
|
|
552d010650 | ||
|
|
1c3106cbb0 | ||
|
|
168937f1b2 | ||
|
|
730cffcb5b | ||
|
|
8c3f1c2aba | ||
|
|
ea375ea76c | ||
|
|
275a6c0c31 | ||
|
|
360204995d | ||
|
|
c9fc0cc75a | ||
|
|
41daaa322e | ||
|
|
894535fbe5 | ||
|
|
6fc5af1b0f | ||
|
|
5ce806a7d7 | ||
|
|
25cb23fdb3 | ||
|
|
a89a1a2bc9 | ||
|
|
93e170f9ac | ||
|
|
59d9a616aa | ||
|
|
2ff0934206 | ||
|
|
cde7cba2f0 | ||
|
|
2be7501afe | ||
|
|
487d35dae2 | ||
|
|
19af9376cb | ||
|
|
3ddd2a0b33 | ||
|
|
ee7523b124 | ||
|
|
cc1dbac1f0 | ||
|
|
04532ba8a6 | ||
|
|
0a2a132b11 | ||
|
|
3ff712d407 | ||
|
|
27162d2205 | ||
|
|
f150974566 |
23
.travis.yml
23
.travis.yml
@@ -1,37 +1,26 @@
|
|||||||
sudo: required # 超级权限
|
sudo: required # 超级权限
|
||||||
|
|
||||||
dist: trusty # 在ubuntu:trusty
|
dist: trusty # 在ubuntu:trusty
|
||||||
|
|
||||||
language: go # 声明构建语言环境
|
language: go # 声明构建语言环境
|
||||||
|
|
||||||
go: # 只构建最新版本
|
go: # 只构建最新版本
|
||||||
- 1.8
|
- tip
|
||||||
|
services: # docker环境
|
||||||
services: # docker环境
|
|
||||||
- docker
|
- docker
|
||||||
|
|
||||||
branches: # 限定项目分支
|
branches: # 限定项目分支
|
||||||
only:
|
only:
|
||||||
- /^v[0-9](\.[0-9]){2}(-rc[1-9])?$/
|
- /^v[0-9](\.[0-9]){2}(-rc[1-9])?$/
|
||||||
|
|
||||||
install:
|
install:
|
||||||
- curl https://glide.sh/get | sh # 安装glide包管理
|
- curl https://glide.sh/get | sh # 安装glide包管理
|
||||||
|
|
||||||
script:
|
script:
|
||||||
- glide up
|
- glide up
|
||||||
- GOOS=linux GOARCH=amd64 go build # 编译版本
|
- GOOS=linux GOARCH=amd64 CGO_ENABLED=0 go build # 编译版本
|
||||||
- docker build -t registry.cn-hangzhou.aliyuncs.com/deepzz/eiblog . # 构建镜像
|
- docker build -t registry.cn-hangzhou.aliyuncs.com/deepzz/eiblog . # 构建镜像
|
||||||
|
|
||||||
after_success:
|
after_success:
|
||||||
# - if [ "$TRAVIS_BRANCH" =~ ^v[0-9](\.[0-9])+.*$ ]; then
|
- docker login -u="$DOCKER_USERNAME" -p="$DOCKER_PASSWORD" registry.cn-hangzhou.aliyuncs.com
|
||||||
# docker login -u="$DOCKER_USERNAME" -p="$DOCKER_PASSWORD" registry.cn-hangzhou.aliyuncs.com;
|
|
||||||
# docker push registry.cn-hangzhou.aliyuncs.com/deepzz/eiblog;
|
|
||||||
# fi
|
|
||||||
- docker push registry.cn-hangzhou.aliyuncs.com/deepzz/eiblog
|
- docker push registry.cn-hangzhou.aliyuncs.com/deepzz/eiblog
|
||||||
|
- docker tag registry.cn-hangzhou.aliyuncs.com/deepzz/eiblog registry.cn-hangzhou.aliyuncs.com/deepzz/eiblog:$TRAVIS_TAG
|
||||||
|
- docker push registry.cn-hangzhou.aliyuncs.com/deepzz/eiblog:$TRAVIS_TAG
|
||||||
before_deploy:
|
before_deploy:
|
||||||
- ./dist.sh
|
- ./dist.sh
|
||||||
|
|
||||||
deploy:
|
deploy:
|
||||||
provider: releases
|
provider: releases
|
||||||
api_key:
|
api_key:
|
||||||
|
|||||||
39
CHANGELOG.md
39
CHANGELOG.md
@@ -1,5 +1,44 @@
|
|||||||
# Eiblog Changelog
|
# Eiblog Changelog
|
||||||
|
|
||||||
|
## v1.4.2 (2018-02-09)
|
||||||
|
* 修复博客初始化后,about 页面不能够评论 #6
|
||||||
|
* 修复编辑专题,按钮显示“添加专题”错误
|
||||||
|
* 优化“添加文章”从同步改为异步推送:feed,es,disqus。速度显著提升
|
||||||
|
* (**重要*)头像图片从 avatar.jpg 改为 avatar.png(透明)
|
||||||
|
* docker-compose.yml mongodb 去掉端口映射,防止用户将端口暴露至外网
|
||||||
|
* session key 每次重启随机生成等一些细节的修复
|
||||||
|
|
||||||
|
## v1.4.1 (2018-01-14)
|
||||||
|
* 修复创建新文章,disqus 不收录bug
|
||||||
|
* 修复创建新文章,归档页面不刷新bug
|
||||||
|
* 修复能够删除关于页面和友情链接页面bug
|
||||||
|
* 修复重复添加文章错误
|
||||||
|
* 注释掉 docker-compose.yml 自动备份内容,请自行解开
|
||||||
|
* 添加当月数大于12,归档页面使用年份归档
|
||||||
|
* 优化代码逻辑
|
||||||
|
|
||||||
|
## v1.4.0 (2018-01-01)
|
||||||
|
* fix 搜索页面 bug
|
||||||
|
* CGO_ENABLED=0 关闭 cgo
|
||||||
|
* 更新Makefile ct log 服务器
|
||||||
|
* 数据库数据终于可以备份了
|
||||||
|
|
||||||
|
## v1.3.4 (2017-11-29)
|
||||||
|
* fix page:admin/write-post autocomplete tag
|
||||||
|
|
||||||
|
## v1.3.3 (2017-11-27)
|
||||||
|
* fix docker image: exec user process caused "no such file or directory"
|
||||||
|
|
||||||
|
## v1.3.2 (2017-11-17)
|
||||||
|
* 修复文章自动保存引起的发布文章不成功的bug
|
||||||
|
|
||||||
|
## v1.3.1 (2017-11-05)
|
||||||
|
* 修复调整 关于、友情链接 创建时间出现文章乱序
|
||||||
|
* 修复评论时间计算错误
|
||||||
|
* 调整acme文件验证路径
|
||||||
|
* 更改七牛SDK包为github包。
|
||||||
|
* 调整七牛配置文件名称,app.yml: kodo -> qiniu,name -> bucket,请提高静态文件版本 staticversion
|
||||||
|
|
||||||
## v1.3.0 (2017-07-13)
|
## v1.3.0 (2017-07-13)
|
||||||
* 更改 app.yml 配置项,将大部分配置归在 general 常规配置下。注意,部署时请先更新 app.yml。
|
* 更改 app.yml 配置项,将大部分配置归在 general 常规配置下。注意,部署时请先更新 app.yml。
|
||||||
* 静态文件采用动态渲染,即用户不再需要管理 view、static 目录。
|
* 静态文件采用动态渲染,即用户不再需要管理 view、static 目录。
|
||||||
|
|||||||
@@ -7,4 +7,4 @@ ADD static/tzdata/Shanghai /etc/localtime
|
|||||||
COPY . /eiblog
|
COPY . /eiblog
|
||||||
EXPOSE 9000
|
EXPOSE 9000
|
||||||
WORKDIR /eiblog
|
WORKDIR /eiblog
|
||||||
CMD ["./eiblog"]
|
CMD ["sh","-c","/eiblog/eiblog"]
|
||||||
|
|||||||
54
Makefile
54
Makefile
@@ -15,7 +15,7 @@ test:
|
|||||||
build:
|
build:
|
||||||
@echo "go build..."
|
@echo "go build..."
|
||||||
@CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build && \
|
@CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build && \
|
||||||
docker build -t $(docker_registry)/deepzz/eiblog:latest .
|
docker build -t $(docker_registry)/deepzz/eiblog:latest .
|
||||||
|
|
||||||
deploy:build
|
deploy:build
|
||||||
@docker push $(docker_registry)/deepzz/eiblog:latest
|
@docker push $(docker_registry)/deepzz/eiblog:latest
|
||||||
@@ -24,47 +24,37 @@ dist:
|
|||||||
@./dist.sh
|
@./dist.sh
|
||||||
|
|
||||||
gencert:makedir
|
gencert:makedir
|
||||||
@echo $(Ali_Key) $(Ali_Secret)
|
|
||||||
@if [ ! -n "$(sans)" ]; then \
|
@if [ ! -n "$(sans)" ]; then \
|
||||||
printf "Need one argument [sans=params]\n"; \
|
printf "Need one argument [sans=params]\n"; \
|
||||||
printf "example: sans=\"-d domain -d domain\"\n"; \
|
printf "example: sans=\"-d domain -d domain\"\n"; \
|
||||||
exit 1; \
|
exit 1; \
|
||||||
fi; \
|
fi; \
|
||||||
if [ ! -n "$(cn)" ]; then \
|
if [ ! -n "$(cn)" ]; then \
|
||||||
printf "Need one argument [cn=params]\n"; \
|
printf "Need one argument [cn=params]\n"; \
|
||||||
printf "example: cn=domain\n"; \
|
printf "example: cn=domain\n"; \
|
||||||
exit 1; \
|
exit 1; \
|
||||||
fi
|
fi
|
||||||
@if [ ! -f $(acme.sh) ]; then \
|
@if [ ! -f $(acme.sh) ]; then \
|
||||||
curl https://get.acme.sh | sh; \
|
curl https://get.acme.sh | sh; \
|
||||||
fi
|
fi
|
||||||
|
|
||||||
@echo "generate rsa cert..."
|
@echo "generate rsa cert..."
|
||||||
@$(acme.sh) --force --issue --dns dns_ali \
|
@$(acme.sh) --force --issue --dns dns_ali $(sans) --log \
|
||||||
$(sans) --log --renew-hook "ct-submit ctlog.api.venafi.com < $(config)/ssl/domain.rsa.pem > $(config)/scts/rsa/venafi.sct && \
|
--renew-hook "ct-submit ctlog-gen2.api.venafi.com < $(config)/ssl/domain.rsa.pem > $(config)/scts/rsa/venafi.sct \
|
||||||
ct-submit ctlog.wosign.com < $(config)/ssl/domain.rsa.pem > $(config)/scts/rsa/wosign.sct"
|
&& ct-submit ctlog.wosign.com < $(config)/ssl/domain.rsa.pem > $(config)/scts/rsa/wosign.sct"
|
||||||
@$(acme.sh) --install-cert -d $(cn) \
|
@$(acme.sh) --install-cert -d $(cn) \
|
||||||
--key-file $(config)/ssl/domain.rsa.key \
|
--key-file $(config)/ssl/domain.rsa.key \
|
||||||
--fullchain-file $(config)/ssl/domain.rsa.pem \
|
--fullchain-file $(config)/ssl/domain.rsa.pem \
|
||||||
--reloadcmd "service nginx force-reload"
|
--reloadcmd "service nginx force-reload"
|
||||||
|
|
||||||
@echo "generate ecc cert..."
|
@echo "generate ecc cert..."
|
||||||
@$(acme.sh) --force --issue --dns dns_ali \
|
@$(acme.sh) --force --issue --dns dns_ali $(sans) -k ec-256 --log \
|
||||||
$(sans) -k ec-256 --log --renew-hook "ct-submit ctlog.api.venafi.com < $(config)/ssl/domain.ecc.pem > $(config)/scts/ecc/venafi.sct && \
|
--renew-hook "ct-submit ctlog-gen2.api.venafi.com < $(config)/ssl/domain.ecc.pem > $(config)/scts/ecc/venafi.sct \
|
||||||
ct-submit ctlog.wosign.com < $(config)/ssl/domain.ecc.pem > $(config)/scts/ecc/wosign.sct"
|
&& ct-submit ctlog.wosign.com < $(config)/ssl/domain.ecc.pem > $(config)/scts/ecc/wosign.sct"
|
||||||
@$(acme.sh) --install-cert -d $(cn) --ecc \
|
@$(acme.sh) --install-cert -d $(cn) --ecc \
|
||||||
--key-file $(config)/ssl/domain.ecc.key \
|
--key-file $(config)/ssl/domain.ecc.key \
|
||||||
--fullchain-file $(config)/ssl/domain.ecc.pem \
|
--fullchain-file $(config)/ssl/domain.ecc.pem \
|
||||||
--reloadcmd "service nginx force-reload"
|
--reloadcmd "service nginx force-reload"
|
||||||
|
|
||||||
# fullchained:
|
|
||||||
# @if [ ! -n "$(cn)" ]; then \
|
|
||||||
# printf "Use acme.sh generated certs, Need one argument [cn=params]\n"; \
|
|
||||||
# printf "example: cn=domain\n"; \
|
|
||||||
# exit 1; \
|
|
||||||
# fi
|
|
||||||
# @cp $(acme)/$(cn)/ca.cer $(config)/ssl/full_chained.pem && \
|
|
||||||
# echo $(X3) >> $(config)/ssl/full_chained.pem
|
|
||||||
|
|
||||||
dhparams:
|
dhparams:
|
||||||
@openssl dhparam -out $(config)/ssl/dhparams.pem 2048
|
@openssl dhparam -out $(config)/ssl/dhparams.pem 2048
|
||||||
|
|||||||
14
README.md
14
README.md
@@ -1,4 +1,4 @@
|
|||||||
# EiBlog [](https://travis-ci.org/eiblog/eiblog) [](LICENSE.md) [](https://github.com/eiblog/eiblog/releases)
|
# EiBlog [](https://travis-ci.org/eiblog/eiblog) [](LICENSE.md) [](https://github.com/eiblog/eiblog/releases)
|
||||||
|
|
||||||
> 系统根据[https://imququ.com](https://imququ.com)一系列文章和方向进行搭建,期间获得了QuQu的很大帮助,在此表示感谢。
|
> 系统根据[https://imququ.com](https://imququ.com)一系列文章和方向进行搭建,期间获得了QuQu的很大帮助,在此表示感谢。
|
||||||
|
|
||||||
@@ -11,8 +11,8 @@
|
|||||||
整个博客系统涉及到模块如下:
|
整个博客系统涉及到模块如下:
|
||||||
|
|
||||||
* 自动更新证书:
|
* 自动更新证书:
|
||||||
* 接入 [acme/autocert](https://github.com/golang/crypto/tree/master/acme/autocert),在 TLS 层开启全自动更新证书,从此证书的更新再也不用惦记了。
|
* 接入 [acme/autocert](https://github.com/golang/crypto/tree/master/acme/autocert),在 TLS 层开启全自动更新证书,从此证书的更新再也不用惦记了,不过 Go 的 HTTPS 兼容性不够好(不想兼容),在如部分 IE 和 UC 之类的浏览器不能访问,请悉知。
|
||||||
* 如果你采用如 Nginx 代理,你可能需要 [acme.sh](https://github.com/Neilpang/acme.sh) 实现证书的自动。博主实现 aliyun dns 的自动验证方式,详见 [Makefile/gencert](https://github.com/eiblog/eiblog/blob/master/Makefile)。
|
* 如果你采用如 Nginx 代理,推荐使用 [acme.sh](https://github.com/Neilpang/acme.sh) 实现证书的自动部署。博主实现 aliyun dns 的自动验证方式,详见 [Makefile/gencert](https://github.com/eiblog/eiblog/blob/master/Makefile)。
|
||||||
* `MongoDB`,博客采用 mongodb 作为存储数据库。
|
* `MongoDB`,博客采用 mongodb 作为存储数据库。
|
||||||
* `Elasticsearch`,采用 `elasticsearch` 作为博客的站内搜索,尽管占用内存稍高。
|
* `Elasticsearch`,采用 `elasticsearch` 作为博客的站内搜索,尽管占用内存稍高。
|
||||||
* `Disqus`,作为博客评论系统,国内大部分被墙,故实现两种评论方式。
|
* `Disqus`,作为博客评论系统,国内大部分被墙,故实现两种评论方式。
|
||||||
@@ -65,8 +65,8 @@
|
|||||||
* `CT`,证书透明度检测,提供一个开放的审计和监控系统。可以让任何域名所有者或者 CA 确定证书是否被错误签发或者被恶意使用,从而提高 HTTPS 网站的安全性。
|
* `CT`,证书透明度检测,提供一个开放的审计和监控系统。可以让任何域名所有者或者 CA 确定证书是否被错误签发或者被恶意使用,从而提高 HTTPS 网站的安全性。
|
||||||
* `OSCP`,在线证书状态协议。用来检验证书合法性的在线查询服务.
|
* `OSCP`,在线证书状态协议。用来检验证书合法性的在线查询服务.
|
||||||
* `HSTS`,强制客户端(如浏览器)使用 HTTPS 与服务器创建连接。可以很好的解决 HTTPS 降级攻击。
|
* `HSTS`,强制客户端(如浏览器)使用 HTTPS 与服务器创建连接。可以很好的解决 HTTPS 降级攻击。
|
||||||
* `HPKP`,HTTP 公钥固定扩展,防范由「伪造或不正当手段获得网站证书」造成的中间人攻击。该功能让我们选择信任哪些`CA`。
|
* `HPKP`,HTTP 公钥固定扩展,防范由「伪造或不正当手段获得网站证书」造成的中间人攻击。该功能让我们选择信任哪些`CA`。请不要轻易尝试 Nginx 线上运行,因为该配置目前只指定了 Letsencrypt X3 和 TrustAsia G5 证书 pin-sha256。
|
||||||
* `SSL Protocols`,罗列支持的 `TLS` 协议,SSLv3 被证实是不安全的。
|
* `SSL Protocols`,罗列支持的 `TLS` 协议,SSLv3 被证实是不安全的。
|
||||||
* `SSL dhparam`,迪菲赫尔曼密钥交换。
|
* `SSL dhparam`,迪菲赫尔曼密钥交换。
|
||||||
* `Cipher suite`,罗列服务器支持加密套件。
|
* `Cipher suite`,罗列服务器支持加密套件。
|
||||||
6. 文章评论数量(不重要)后端跑定时脚本,定时更新,所以有时评论数是不对的。这样减少了 api 调用,又再次达到加速访问的目的。
|
6. 文章评论数量(不重要)后端跑定时脚本,定时更新,所以有时评论数是不对的。这样减少了 api 调用,又再次达到加速访问的目的。
|
||||||
@@ -74,6 +74,7 @@
|
|||||||
8. 开源 `Typecho` 完整后台系统,全功能 `markdown` 编辑器,让你体验什么是简洁清爽。
|
8. 开源 `Typecho` 完整后台系统,全功能 `markdown` 编辑器,让你体验什么是简洁清爽。
|
||||||
9. 博客后台直接对接 `七牛 SDK`,实现后台上传文件和删除文件的简单功能。
|
9. 博客后台直接对接 `七牛 SDK`,实现后台上传文件和删除文件的简单功能。
|
||||||
10. 采用 `elasticsearch` 作为站内搜索,添加 `google opensearch` 功能,搜索更加自然。
|
10. 采用 `elasticsearch` 作为站内搜索,添加 `google opensearch` 功能,搜索更加自然。
|
||||||
|
11. 自动备份数据库数据到七牛云。
|
||||||
|
|
||||||
### 文档
|
### 文档
|
||||||
|
|
||||||
@@ -81,9 +82,10 @@
|
|||||||
* [安装部署](https://github.com/eiblog/eiblog/blob/master/docs/install.md)
|
* [安装部署](https://github.com/eiblog/eiblog/blob/master/docs/install.md)
|
||||||
* [写作需知](https://github.com/eiblog/eiblog/blob/master/docs/writing.md)
|
* [写作需知](https://github.com/eiblog/eiblog/blob/master/docs/writing.md)
|
||||||
* [好玩的功能](https://github.com/eiblog/eiblog/blob/master/docs/amusing.md)
|
* [好玩的功能](https://github.com/eiblog/eiblog/blob/master/docs/amusing.md)
|
||||||
|
* [关于备份](https://github.com/eiblog/backup)
|
||||||
|
|
||||||
### 成功搭建者博客
|
### 成功搭建者博客
|
||||||
|
|
||||||
* [https://razeencheng.com/](https://razeencheng.com/) - Razeen's Blog
|
* [https://razeen.me](https://razeen.me) - Razeen's Blog
|
||||||
|
|
||||||
如果你的博客使用`Eiblog`搭建,你可以在 [这里](https://github.com/eiblog/eiblog/issues/1) 提交网址。
|
如果你的博客使用`Eiblog`搭建,你可以在 [这里](https://github.com/eiblog/eiblog/issues/1) 提交网址。
|
||||||
|
|||||||
207
api.go
207
api.go
@@ -4,23 +4,26 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
"sort"
|
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/eiblog/eiblog/setting"
|
"github.com/eiblog/eiblog/setting"
|
||||||
"github.com/eiblog/utils/logd"
|
"github.com/eiblog/utils/logd"
|
||||||
|
"github.com/eiblog/utils/mgo"
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"gopkg.in/mgo.v2/bson"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
// 成功
|
||||||
NOTICE_SUCCESS = "success"
|
NOTICE_SUCCESS = "success"
|
||||||
NOTICE_NOTICE = "notice"
|
// 注意
|
||||||
NOTICE_ERROR = "error"
|
NOTICE_NOTICE = "notice"
|
||||||
|
// 错误
|
||||||
|
NOTICE_ERROR = "error"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// 全局 API
|
||||||
var APIs = make(map[string]func(c *gin.Context))
|
var APIs = make(map[string]func(c *gin.Context))
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
@@ -52,6 +55,7 @@ func init() {
|
|||||||
APIs["file-delete"] = apiFileDelete
|
APIs["file-delete"] = apiFileDelete
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 更新账号信息,Email、PhoneNumber、Address
|
||||||
func apiAccount(c *gin.Context) {
|
func apiAccount(c *gin.Context) {
|
||||||
e := c.PostForm("email")
|
e := c.PostForm("email")
|
||||||
pn := c.PostForm("phoneNumber")
|
pn := c.PostForm("phoneNumber")
|
||||||
@@ -61,8 +65,10 @@ func apiAccount(c *gin.Context) {
|
|||||||
responseNotice(c, NOTICE_NOTICE, "参数错误", "")
|
responseNotice(c, NOTICE_NOTICE, "参数错误", "")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
err := UpdateAccountField(bson.M{"$set": bson.M{"email": e, "phonen": pn, "address": ad}})
|
|
||||||
|
err := UpdateAccountField(mgo.M{"$set": mgo.M{"email": e, "phonen": pn, "address": ad}})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
logd.Error(err)
|
||||||
responseNotice(c, NOTICE_NOTICE, err.Error(), "")
|
responseNotice(c, NOTICE_NOTICE, err.Error(), "")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -72,6 +78,7 @@ func apiAccount(c *gin.Context) {
|
|||||||
responseNotice(c, NOTICE_SUCCESS, "更新成功", "")
|
responseNotice(c, NOTICE_SUCCESS, "更新成功", "")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 更新博客信息
|
||||||
func apiBlog(c *gin.Context) {
|
func apiBlog(c *gin.Context) {
|
||||||
bn := c.PostForm("blogName")
|
bn := c.PostForm("blogName")
|
||||||
bt := c.PostForm("bTitle")
|
bt := c.PostForm("bTitle")
|
||||||
@@ -83,8 +90,12 @@ func apiBlog(c *gin.Context) {
|
|||||||
responseNotice(c, NOTICE_NOTICE, "参数错误", "")
|
responseNotice(c, NOTICE_NOTICE, "参数错误", "")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
err := UpdateAccountField(bson.M{"$set": bson.M{"blogger.blogname": bn, "blogger.btitle": bt, "blogger.beian": ba, "blogger.subtitle": st, "blogger.seriessay": ss, "blogger.archivessay": as}})
|
|
||||||
|
err := UpdateAccountField(mgo.M{"$set": mgo.M{"blogger.blogname": bn,
|
||||||
|
"blogger.btitle": bt, "blogger.beian": ba, "blogger.subtitle": st,
|
||||||
|
"blogger.seriessay": ss, "blogger.archivessay": as}})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
logd.Error(err)
|
||||||
responseNotice(c, NOTICE_NOTICE, err.Error(), "")
|
responseNotice(c, NOTICE_NOTICE, err.Error(), "")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -99,6 +110,7 @@ func apiBlog(c *gin.Context) {
|
|||||||
responseNotice(c, NOTICE_SUCCESS, "更新成功", "")
|
responseNotice(c, NOTICE_SUCCESS, "更新成功", "")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 更新密码
|
||||||
func apiPassword(c *gin.Context) {
|
func apiPassword(c *gin.Context) {
|
||||||
logd.Debug(c.Request.PostForm.Encode())
|
logd.Debug(c.Request.PostForm.Encode())
|
||||||
od := c.PostForm("old")
|
od := c.PostForm("old")
|
||||||
@@ -117,8 +129,10 @@ func apiPassword(c *gin.Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
newPwd := EncryptPasswd(Ei.Username, nw)
|
newPwd := EncryptPasswd(Ei.Username, nw)
|
||||||
err := UpdateAccountField(bson.M{"$set": bson.M{"password": newPwd}})
|
|
||||||
|
err := UpdateAccountField(mgo.M{"$set": mgo.M{"password": newPwd}})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
logd.Error(err)
|
||||||
responseNotice(c, NOTICE_NOTICE, err.Error(), "")
|
responseNotice(c, NOTICE_NOTICE, err.Error(), "")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -126,45 +140,39 @@ func apiPassword(c *gin.Context) {
|
|||||||
responseNotice(c, NOTICE_SUCCESS, "更新成功", "")
|
responseNotice(c, NOTICE_SUCCESS, "更新成功", "")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 删除文章,软删除:移入到回收箱
|
||||||
func apiPostDelete(c *gin.Context) {
|
func apiPostDelete(c *gin.Context) {
|
||||||
var err error
|
|
||||||
defer func() {
|
|
||||||
if err != nil {
|
|
||||||
logd.Error(err)
|
|
||||||
responseNotice(c, NOTICE_NOTICE, err.Error(), "")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
responseNotice(c, NOTICE_SUCCESS, "删除成功", "")
|
|
||||||
}()
|
|
||||||
err = c.Request.ParseForm()
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
var ids []int32
|
var ids []int32
|
||||||
var i int
|
for _, v := range c.PostFormArray("cid[]") {
|
||||||
for _, v := range c.Request.PostForm["cid[]"] {
|
i, err := strconv.Atoi(v)
|
||||||
i, err = strconv.Atoi(v)
|
if err != nil || int32(i) < setting.Conf.General.StartID {
|
||||||
if err != nil || i < 1 {
|
responseNotice(c, NOTICE_NOTICE, "参数错误", "")
|
||||||
err = errors.New("参数错误")
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
ids = append(ids, int32(i))
|
ids = append(ids, int32(i))
|
||||||
}
|
}
|
||||||
err = DelArticles(ids...)
|
err := DelArticles(ids...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
logd.Error(err)
|
||||||
|
responseNotice(c, NOTICE_NOTICE, err.Error(), "")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
// elasticsearch 删除索引
|
|
||||||
|
// elasticsearch
|
||||||
err = ElasticDelIndex(ids)
|
err = ElasticDelIndex(ids)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
logd.Error(err)
|
||||||
}
|
}
|
||||||
|
// TODO disqus delete
|
||||||
|
responseNotice(c, NOTICE_SUCCESS, "删除成功", "")
|
||||||
}
|
}
|
||||||
|
|
||||||
func apiPostAdd(c *gin.Context) {
|
func apiPostAdd(c *gin.Context) {
|
||||||
var err error
|
var (
|
||||||
var do string
|
err error
|
||||||
var cid int
|
do string
|
||||||
|
cid int
|
||||||
|
)
|
||||||
defer func() {
|
defer func() {
|
||||||
switch do {
|
switch do {
|
||||||
case "auto": // 自动保存
|
case "auto": // 自动保存
|
||||||
@@ -173,29 +181,28 @@ func apiPostAdd(c *gin.Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
c.JSON(http.StatusOK, gin.H{"success": SUCCESS, "time": time.Now().Format("15:04:05 PM"), "cid": cid})
|
c.JSON(http.StatusOK, gin.H{"success": SUCCESS, "time": time.Now().Format("15:04:05 PM"), "cid": cid})
|
||||||
case "save": // 保存草稿
|
case "save", "publish": // 草稿,发布
|
||||||
if err != nil {
|
if err != nil {
|
||||||
responseNotice(c, NOTICE_NOTICE, err.Error(), "")
|
responseNotice(c, NOTICE_NOTICE, err.Error(), "")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
c.Redirect(http.StatusFound, "/admin/manage-draft")
|
uri := "/admin/manage-draft"
|
||||||
case "publish": // 发布
|
if do == "publish" {
|
||||||
if err != nil {
|
uri = "/admin/manage-posts"
|
||||||
responseNotice(c, NOTICE_NOTICE, err.Error(), "")
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
c.Redirect(http.StatusFound, "/admin/manage-posts")
|
c.Redirect(http.StatusFound, uri)
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
do = c.PostForm("do") // auto or save or publish
|
do = c.PostForm("do") // auto or save or publish
|
||||||
slug := c.PostForm("slug")
|
slug := c.PostForm("slug")
|
||||||
title := c.PostForm("title")
|
title := c.PostForm("title")
|
||||||
text := c.PostForm("text")
|
text := c.PostForm("text")
|
||||||
date := c.PostForm("date")
|
date := CheckDate(c.PostForm("date"))
|
||||||
serie := c.PostForm("serie")
|
serie := c.PostForm("serie")
|
||||||
tag := c.PostForm("tags")
|
tag := c.PostForm("tags")
|
||||||
update := c.PostForm("update")
|
update := c.PostForm("update")
|
||||||
if title == "" || text == "" || slug == "" {
|
if slug == "" || title == "" || text == "" {
|
||||||
err = errors.New("参数错误")
|
err = errors.New("参数错误")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -208,13 +215,14 @@ func apiPostAdd(c *gin.Context) {
|
|||||||
Title: title,
|
Title: title,
|
||||||
Content: text,
|
Content: text,
|
||||||
Slug: slug,
|
Slug: slug,
|
||||||
CreateTime: CheckDate(date),
|
CreateTime: date,
|
||||||
IsDraft: do != "publish",
|
IsDraft: do != "publish",
|
||||||
Author: Ei.Username,
|
Author: Ei.Username,
|
||||||
SerieID: serieid,
|
SerieID: serieid,
|
||||||
Tags: tags,
|
Tags: tags,
|
||||||
}
|
}
|
||||||
cid, err = strconv.Atoi(c.PostForm("cid"))
|
cid, err = strconv.Atoi(c.PostForm("cid"))
|
||||||
|
// 新文章
|
||||||
if err != nil || cid < 1 {
|
if err != nil || cid < 1 {
|
||||||
err = AddArticle(artc)
|
err = AddArticle(artc)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -223,58 +231,55 @@ func apiPostAdd(c *gin.Context) {
|
|||||||
}
|
}
|
||||||
cid = int(artc.ID)
|
cid = int(artc.ID)
|
||||||
if !artc.IsDraft {
|
if !artc.IsDraft {
|
||||||
ElasticIndex(artc)
|
// 异步执行,快
|
||||||
DoPings(slug)
|
go func() {
|
||||||
|
// elastic
|
||||||
|
ElasticIndex(artc)
|
||||||
|
// rss
|
||||||
|
DoPings(slug)
|
||||||
|
// disqus
|
||||||
|
ThreadCreate(artc)
|
||||||
|
}()
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 旧文章
|
||||||
artc.ID = int32(cid)
|
artc.ID = int32(cid)
|
||||||
i, a := GetArticle(artc.ID)
|
_, a := GetArticle(artc.ID)
|
||||||
if a != nil {
|
if a != nil {
|
||||||
artc.IsDraft = false
|
artc.IsDraft = false
|
||||||
artc.Count = a.Count
|
artc.Count = a.Count
|
||||||
artc.UpdateTime = a.UpdateTime
|
artc.UpdateTime = a.UpdateTime
|
||||||
Ei.Articles = append(Ei.Articles[0:i], Ei.Articles[i+1:]...)
|
|
||||||
DelFromLinkedList(a)
|
|
||||||
ManageTagsArticle(a, false, DELETE)
|
|
||||||
ManageSeriesArticle(a, false, DELETE)
|
|
||||||
ManageArchivesArticle(a, false, DELETE)
|
|
||||||
delete(Ei.MapArticles, a.Slug)
|
|
||||||
a = nil
|
|
||||||
}
|
}
|
||||||
if CheckBool(update) {
|
if CheckBool(update) {
|
||||||
artc.UpdateTime = time.Now()
|
artc.UpdateTime = time.Now()
|
||||||
}
|
}
|
||||||
err = UpdateArticle(bson.M{"id": artc.ID}, artc)
|
// 数据库更新
|
||||||
|
err = UpdateArticle(mgo.M{"id": artc.ID}, artc)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logd.Error(err)
|
logd.Error(err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if !artc.IsDraft {
|
if !artc.IsDraft {
|
||||||
Ei.MapArticles[artc.Slug] = artc
|
ReplaceArticle(a, artc)
|
||||||
Ei.Articles = append(Ei.Articles, artc)
|
// 异步执行,快
|
||||||
sort.Sort(Ei.Articles)
|
go func() {
|
||||||
GenerateExcerptAndRender(artc)
|
// elastic
|
||||||
// elasticsearch 索引
|
ElasticIndex(artc)
|
||||||
ElasticIndex(artc)
|
// rss
|
||||||
DoPings(slug)
|
DoPings(slug)
|
||||||
if artc.ID >= setting.Conf.General.StartID {
|
// disqus
|
||||||
ManageTagsArticle(artc, true, ADD)
|
if a == nil {
|
||||||
ManageSeriesArticle(artc, true, ADD)
|
ThreadCreate(artc)
|
||||||
ManageArchivesArticle(artc, true, ADD)
|
}
|
||||||
AddToLinkedList(artc.ID)
|
}()
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 只能逐一删除,专题下不能有文章
|
||||||
func apiSerieDelete(c *gin.Context) {
|
func apiSerieDelete(c *gin.Context) {
|
||||||
err := c.Request.ParseForm()
|
for _, v := range c.PostFormArray("mid[]") {
|
||||||
if err != nil {
|
|
||||||
responseNotice(c, NOTICE_NOTICE, err.Error(), "")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
// 只能逐一删除
|
|
||||||
for _, v := range c.Request.PostForm["mid[]"] {
|
|
||||||
id, err := strconv.Atoi(v)
|
id, err := strconv.Atoi(v)
|
||||||
if err != nil || id < 1 {
|
if err != nil || id < 1 {
|
||||||
responseNotice(c, NOTICE_NOTICE, err.Error(), "")
|
responseNotice(c, NOTICE_NOTICE, err.Error(), "")
|
||||||
@@ -290,6 +295,7 @@ func apiSerieDelete(c *gin.Context) {
|
|||||||
responseNotice(c, NOTICE_SUCCESS, "删除成功", "")
|
responseNotice(c, NOTICE_SUCCESS, "删除成功", "")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 添加专题,如果专题有提交 mid 即更新专题
|
||||||
func apiSerieAdd(c *gin.Context) {
|
func apiSerieAdd(c *gin.Context) {
|
||||||
name := c.PostForm("name")
|
name := c.PostForm("name")
|
||||||
slug := c.PostForm("slug")
|
slug := c.PostForm("slug")
|
||||||
@@ -326,24 +332,15 @@ func apiSerieAdd(c *gin.Context) {
|
|||||||
responseNotice(c, NOTICE_SUCCESS, "操作成功", "")
|
responseNotice(c, NOTICE_SUCCESS, "操作成功", "")
|
||||||
}
|
}
|
||||||
|
|
||||||
// 暂未启用
|
// NOTE 排序专题,暂未实现
|
||||||
func apiSerieSort(c *gin.Context) {
|
func apiSerieSort(c *gin.Context) {
|
||||||
err := c.Request.ParseForm()
|
v := c.PostFormArray("mid[]")
|
||||||
if err != nil {
|
|
||||||
responseNotice(c, NOTICE_NOTICE, err.Error(), "")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
v := c.Request.PostForm["mid[]"]
|
|
||||||
logd.Debug(v)
|
logd.Debug(v)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 删除草稿箱,物理删除
|
||||||
func apiDraftDelete(c *gin.Context) {
|
func apiDraftDelete(c *gin.Context) {
|
||||||
err := c.Request.ParseForm()
|
for _, v := range c.PostFormArray("mid[]") {
|
||||||
if err != nil {
|
|
||||||
responseNotice(c, NOTICE_NOTICE, err.Error(), "")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
for _, v := range c.Request.PostForm["mid[]"] {
|
|
||||||
i, err := strconv.Atoi(v)
|
i, err := strconv.Atoi(v)
|
||||||
if err != nil || i < 1 {
|
if err != nil || i < 1 {
|
||||||
responseNotice(c, NOTICE_NOTICE, "参数错误", "")
|
responseNotice(c, NOTICE_NOTICE, "参数错误", "")
|
||||||
@@ -358,15 +355,9 @@ func apiDraftDelete(c *gin.Context) {
|
|||||||
responseNotice(c, NOTICE_SUCCESS, "删除成功", "")
|
responseNotice(c, NOTICE_SUCCESS, "删除成功", "")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 删除垃圾箱,物理删除
|
||||||
func apiTrashDelete(c *gin.Context) {
|
func apiTrashDelete(c *gin.Context) {
|
||||||
logd.Debug(c.PostForm("key"))
|
for _, v := range c.PostFormArray("mid[]") {
|
||||||
logd.Debug(c.Request.PostForm)
|
|
||||||
err := c.Request.ParseForm()
|
|
||||||
if err != nil {
|
|
||||||
responseNotice(c, NOTICE_NOTICE, err.Error(), "")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
for _, v := range c.Request.PostForm["mid[]"] {
|
|
||||||
i, err := strconv.Atoi(v)
|
i, err := strconv.Atoi(v)
|
||||||
if err != nil || i < 1 {
|
if err != nil || i < 1 {
|
||||||
responseNotice(c, NOTICE_NOTICE, "参数错误", "")
|
responseNotice(c, NOTICE_NOTICE, "参数错误", "")
|
||||||
@@ -381,15 +372,9 @@ func apiTrashDelete(c *gin.Context) {
|
|||||||
responseNotice(c, NOTICE_SUCCESS, "删除成功", "")
|
responseNotice(c, NOTICE_SUCCESS, "删除成功", "")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 从垃圾箱恢复到草稿箱
|
||||||
func apiTrashRecover(c *gin.Context) {
|
func apiTrashRecover(c *gin.Context) {
|
||||||
logd.Debug(c.PostForm("key"))
|
for _, v := range c.PostFormArray("mid[]") {
|
||||||
logd.Debug(c.Request.PostForm)
|
|
||||||
err := c.Request.ParseForm()
|
|
||||||
if err != nil {
|
|
||||||
responseNotice(c, NOTICE_NOTICE, err.Error(), "")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
for _, v := range c.Request.PostForm["mid[]"] {
|
|
||||||
i, err := strconv.Atoi(v)
|
i, err := strconv.Atoi(v)
|
||||||
if err != nil || i < 1 {
|
if err != nil || i < 1 {
|
||||||
responseNotice(c, NOTICE_NOTICE, "参数错误", "")
|
responseNotice(c, NOTICE_NOTICE, "参数错误", "")
|
||||||
@@ -405,6 +390,7 @@ func apiTrashRecover(c *gin.Context) {
|
|||||||
responseNotice(c, NOTICE_SUCCESS, "恢复成功", "")
|
responseNotice(c, NOTICE_SUCCESS, "恢复成功", "")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 上传文件到 qiniu 云
|
||||||
func apiFileUpload(c *gin.Context) {
|
func apiFileUpload(c *gin.Context) {
|
||||||
type Size interface {
|
type Size interface {
|
||||||
Size() int64
|
Size() int64
|
||||||
@@ -428,7 +414,7 @@ func apiFileUpload(c *gin.Context) {
|
|||||||
c.String(http.StatusBadRequest, err.Error())
|
c.String(http.StatusBadRequest, err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
typ := c.Request.Header.Get("Content-Type")
|
typ := header.Header.Get("Content-Type")
|
||||||
c.JSON(http.StatusOK, gin.H{
|
c.JSON(http.StatusOK, gin.H{
|
||||||
"title": filename,
|
"title": filename,
|
||||||
"isImage": typ[:5] == "image",
|
"isImage": typ[:5] == "image",
|
||||||
@@ -437,20 +423,19 @@ func apiFileUpload(c *gin.Context) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 删除七牛 CDN 文件
|
||||||
func apiFileDelete(c *gin.Context) {
|
func apiFileDelete(c *gin.Context) {
|
||||||
var err error
|
defer c.String(http.StatusOK, "删掉了吗?鬼知道。。。")
|
||||||
defer func() {
|
|
||||||
if err != nil {
|
|
||||||
logd.Error(err)
|
|
||||||
}
|
|
||||||
c.String(http.StatusOK, "删掉了吗?鬼知道。。。")
|
|
||||||
}()
|
|
||||||
name := c.PostForm("title")
|
name := c.PostForm("title")
|
||||||
if name == "" {
|
if name == "" {
|
||||||
err = errors.New("参数错误")
|
logd.Error("参数错误")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
err = FileDelete(name)
|
err := FileDelete(name)
|
||||||
|
if err != nil {
|
||||||
|
logd.Error(err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func responseNotice(c *gin.Context, typ, content, hl string) {
|
func responseNotice(c *gin.Context, typ, content, hl string) {
|
||||||
|
|||||||
18
back.go
18
back.go
@@ -3,6 +3,7 @@ package main
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"html/template"
|
"html/template"
|
||||||
"net/http"
|
"net/http"
|
||||||
@@ -11,11 +12,12 @@ import (
|
|||||||
|
|
||||||
"github.com/eiblog/eiblog/setting"
|
"github.com/eiblog/eiblog/setting"
|
||||||
"github.com/eiblog/utils/logd"
|
"github.com/eiblog/utils/logd"
|
||||||
|
"github.com/eiblog/utils/mgo"
|
||||||
"github.com/gin-gonic/contrib/sessions"
|
"github.com/gin-gonic/contrib/sessions"
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"gopkg.in/mgo.v2/bson"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// 是否登录
|
||||||
func isLogin(c *gin.Context) bool {
|
func isLogin(c *gin.Context) bool {
|
||||||
session := sessions.Default(c)
|
session := sessions.Default(c)
|
||||||
v := session.Get("username")
|
v := session.Get("username")
|
||||||
@@ -25,6 +27,7 @@ func isLogin(c *gin.Context) bool {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 登陆过滤
|
||||||
func AuthFilter() gin.HandlerFunc {
|
func AuthFilter() gin.HandlerFunc {
|
||||||
return func(c *gin.Context) {
|
return func(c *gin.Context) {
|
||||||
if !isLogin(c) {
|
if !isLogin(c) {
|
||||||
@@ -51,6 +54,7 @@ func HandleLogin(c *gin.Context) {
|
|||||||
RenderHTMLBack(c, "login.html", gin.H{"BTitle": Ei.BTitle})
|
RenderHTMLBack(c, "login.html", gin.H{"BTitle": Ei.BTitle})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 登陆接口
|
||||||
func HandleLoginPost(c *gin.Context) {
|
func HandleLoginPost(c *gin.Context) {
|
||||||
user := c.PostForm("user")
|
user := c.PostForm("user")
|
||||||
pwd := c.PostForm("password")
|
pwd := c.PostForm("password")
|
||||||
@@ -61,7 +65,7 @@ func HandleLoginPost(c *gin.Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
if Ei.Username != user || !VerifyPasswd(Ei.Password, user, pwd) {
|
if Ei.Username != user || !VerifyPasswd(Ei.Password, user, pwd) {
|
||||||
logd.Printf("账号或密码错误 %s, %s", user, pwd)
|
logd.Printf("账号或密码错误 %s, %s\n", user, pwd)
|
||||||
c.Redirect(http.StatusFound, "/admin/login")
|
c.Redirect(http.StatusFound, "/admin/login")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -70,12 +74,12 @@ func HandleLoginPost(c *gin.Context) {
|
|||||||
session.Save()
|
session.Save()
|
||||||
Ei.LoginIP = c.ClientIP()
|
Ei.LoginIP = c.ClientIP()
|
||||||
Ei.LoginTime = time.Now()
|
Ei.LoginTime = time.Now()
|
||||||
UpdateAccountField(bson.M{"$set": bson.M{"loginip": Ei.LoginIP, "logintime": Ei.LoginTime}})
|
UpdateAccountField(mgo.M{"$set": mgo.M{"loginip": Ei.LoginIP, "logintime": Ei.LoginTime}})
|
||||||
c.Redirect(http.StatusFound, "/admin/profile")
|
c.Redirect(http.StatusFound, "/admin/profile")
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetBack() gin.H {
|
func GetBack() gin.H {
|
||||||
return gin.H{"Author": Ei.Username, "Kodo": setting.Conf.Kodo}
|
return gin.H{"Author": Ei.Username, "Qiniu": setting.Conf.Qiniu}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 个人配置
|
// 个人配置
|
||||||
@@ -115,11 +119,13 @@ func HandlePost(c *gin.Context) {
|
|||||||
for tag, _ := range Ei.Tags {
|
for tag, _ := range Ei.Tags {
|
||||||
tags = append(tags, T{tag, tag})
|
tags = append(tags, T{tag, tag})
|
||||||
}
|
}
|
||||||
h["Tags"] = tags
|
str, _ := json.Marshal(tags)
|
||||||
|
h["Tags"] = string(str)
|
||||||
c.Status(http.StatusOK)
|
c.Status(http.StatusOK)
|
||||||
RenderHTMLBack(c, "admin-post", h)
|
RenderHTMLBack(c, "admin-post", h)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 删除草稿
|
||||||
func HandleDraftDelete(c *gin.Context) {
|
func HandleDraftDelete(c *gin.Context) {
|
||||||
id, err := strconv.Atoi(c.Query("cid"))
|
id, err := strconv.Atoi(c.Query("cid"))
|
||||||
if err != nil || id < 1 {
|
if err != nil || id < 1 {
|
||||||
@@ -184,6 +190,7 @@ func HandleSeries(c *gin.Context) {
|
|||||||
RenderHTMLBack(c, "admin-series", h)
|
RenderHTMLBack(c, "admin-series", h)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 编辑专题
|
||||||
func HandleSerie(c *gin.Context) {
|
func HandleSerie(c *gin.Context) {
|
||||||
h := GetBack()
|
h := GetBack()
|
||||||
id, err := strconv.Atoi(c.Query("mid"))
|
id, err := strconv.Atoi(c.Query("mid"))
|
||||||
@@ -276,6 +283,7 @@ func HandleAPI(c *gin.Context) {
|
|||||||
api(c)
|
api(c)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 渲染 html
|
||||||
func RenderHTMLBack(c *gin.Context, name string, data gin.H) {
|
func RenderHTMLBack(c *gin.Context, name string, data gin.H) {
|
||||||
if name == "login.html" {
|
if name == "login.html" {
|
||||||
err := Tmpl.ExecuteTemplate(c.Writer, name, data)
|
err := Tmpl.ExecuteTemplate(c.Writer, name, data)
|
||||||
|
|||||||
7
check.go
7
check.go
@@ -6,25 +6,30 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// 检查 email
|
||||||
func CheckEmail(e string) bool {
|
func CheckEmail(e string) bool {
|
||||||
reg := regexp.MustCompile(`^(\w)+([\.\-]\w+)*@(\w)+((\.\w+)+)$`)
|
reg := regexp.MustCompile(`^(\w)+([\.\-]\w+)*@(\w)+((\.\w+)+)$`)
|
||||||
return reg.MatchString(e)
|
return reg.MatchString(e)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 检查 domain
|
||||||
func CheckDomain(domain string) bool {
|
func CheckDomain(domain string) bool {
|
||||||
reg := regexp.MustCompile(`^(http://|https://)?[0-9a-zA-Z]+[0-9a-zA-Z\.-]*\.[a-zA-Z]{2,4}$`)
|
reg := regexp.MustCompile(`^(http://|https://)?[0-9a-zA-Z]+[0-9a-zA-Z\.-]*\.[a-zA-Z]{2,4}$`)
|
||||||
return reg.MatchString(domain)
|
return reg.MatchString(domain)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 检查 sms
|
||||||
func CheckSMS(sms string) bool {
|
func CheckSMS(sms string) bool {
|
||||||
reg := regexp.MustCompile(`^\+\d+$`)
|
reg := regexp.MustCompile(`^\+\d+$`)
|
||||||
return reg.MatchString(sms)
|
return reg.MatchString(sms)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 检查 password
|
||||||
func CheckPwd(pwd string) bool {
|
func CheckPwd(pwd string) bool {
|
||||||
return len(pwd) > 5 && len(pwd) < 19
|
return len(pwd) > 5 && len(pwd) < 19
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 检查日期
|
||||||
func CheckDate(date string) time.Time {
|
func CheckDate(date string) time.Time {
|
||||||
if t, err := time.ParseInLocation("2006-01-02 15:04", date, time.Local); err == nil {
|
if t, err := time.ParseInLocation("2006-01-02 15:04", date, time.Local); err == nil {
|
||||||
return t
|
return t
|
||||||
@@ -32,6 +37,7 @@ func CheckDate(date string) time.Time {
|
|||||||
return time.Now()
|
return time.Now()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 检查 id
|
||||||
func CheckSerieID(sid string) int32 {
|
func CheckSerieID(sid string) int32 {
|
||||||
if id, err := strconv.Atoi(sid); err == nil {
|
if id, err := strconv.Atoi(sid); err == nil {
|
||||||
return int32(id)
|
return int32(id)
|
||||||
@@ -39,6 +45,7 @@ func CheckSerieID(sid string) int32 {
|
|||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// bool
|
||||||
func CheckBool(str string) bool {
|
func CheckBool(str string) bool {
|
||||||
return str == "true" || str == "1"
|
return str == "true" || str == "1"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -35,13 +35,14 @@ general:
|
|||||||
clean: 1
|
clean: 1
|
||||||
# 评论相关
|
# 评论相关
|
||||||
disqus:
|
disqus:
|
||||||
shortname: deepzz
|
shortname: xxxxxx
|
||||||
publickey: wdSgxRm9rdGAlLKFcFdToBe3GT4SibmV7Y8EjJQ0r4GWXeKtxpopMAeIeoI2dTEg
|
publickey: wdSgxRm9rdGAlLKFcFdToBe3GT4SibmV7Y8EjJQ0r4GWXeKtxpopMAeIeoI2dTEg
|
||||||
accesstoken: 50023908f39f4607957e909b495326af
|
accesstoken: 50023908f39f4607957e909b495326af
|
||||||
postscount: https://disqus.com/api/3.0/threads/set.json
|
postscount: https://disqus.com/api/3.0/threads/set.json
|
||||||
postslist: https://disqus.com/api/3.0/threads/listPosts.json
|
postslist: https://disqus.com/api/3.0/threads/listPosts.json
|
||||||
postcreate: https://disqus.com/api/3.0/posts/create.json
|
postcreate: https://disqus.com/api/3.0/posts/create.json
|
||||||
postapprove: https://disqus.com/api/3.0/posts/approve.json
|
postapprove: https://disqus.com/api/3.0/posts/approve.json
|
||||||
|
threadcreate: https://disqus.com/api/3.0/threads/create.json
|
||||||
# disqus.js 文件名
|
# disqus.js 文件名
|
||||||
embed: disqus_7d3cf2.js
|
embed: disqus_7d3cf2.js
|
||||||
# 获取评论数量间隔
|
# 获取评论数量间隔
|
||||||
@@ -49,12 +50,12 @@ disqus:
|
|||||||
# 谷歌统计
|
# 谷歌统计
|
||||||
google:
|
google:
|
||||||
url: https://www.google-analytics.com/collect
|
url: https://www.google-analytics.com/collect
|
||||||
tid: UA-77251712-1
|
tid: UA-xxxxxx-1
|
||||||
v: "1"
|
v: "1"
|
||||||
t: pageview
|
t: pageview
|
||||||
# 七牛CDN
|
# 七牛CDN
|
||||||
kodo:
|
qiniu:
|
||||||
name: eiblog
|
bucket: eiblog
|
||||||
domain: st.deepzz.com
|
domain: st.deepzz.com
|
||||||
accesskey: MB6AXl_Sj_mmFsL-Lt59Dml2Vmy2o8XMmiCbbSeC
|
accesskey: MB6AXl_Sj_mmFsL-Lt59Dml2Vmy2o8XMmiCbbSeC
|
||||||
secretkey: BIrMy0fsZ0_SHNceNXk3eDuo7WmVYzj2-zrmd5Tf
|
secretkey: BIrMy0fsZ0_SHNceNXk3eDuo7WmVYzj2-zrmd5Tf
|
||||||
|
|||||||
0
conf/es/config/scripts/empty.groovy
Normal file
0
conf/es/config/scripts/empty.groovy
Normal file
@@ -65,28 +65,6 @@ server {
|
|||||||
expires 1d;
|
expires 1d;
|
||||||
}
|
}
|
||||||
|
|
||||||
# imququ 的上传文件相关,未用到
|
|
||||||
location ^~ /static/uploads/ {
|
|
||||||
root /home/jerry/www/imququ.com/www;
|
|
||||||
add_header Access-Control-Allow-Origin *;
|
|
||||||
|
|
||||||
set $expires_time max;
|
|
||||||
|
|
||||||
valid_referers blocked none server_names *.qgy18.com *.inoreader.com feedly.com *.feedly.com www.udpwork.com theoldreader.com digg.com *.feiworks.com *.newszeit.com r.mail.qq.com yuedu.163.com *.w3ctech.com;
|
|
||||||
if ($invalid_referer) {
|
|
||||||
set $expires_time -1;
|
|
||||||
return 403;
|
|
||||||
}
|
|
||||||
|
|
||||||
expires $expires_time;
|
|
||||||
}
|
|
||||||
|
|
||||||
location ^~ /static/ {
|
|
||||||
root /data/eiblog;
|
|
||||||
add_header Access-Control-Allow-Origin *;
|
|
||||||
expires max;
|
|
||||||
}
|
|
||||||
|
|
||||||
location ^~ /admin/ {
|
location ^~ /admin/ {
|
||||||
proxy_http_version 1.1;
|
proxy_http_version 1.1;
|
||||||
|
|
||||||
@@ -95,7 +73,7 @@ server {
|
|||||||
# deny 将完全不允许页面被嵌套,可能会导致一些异常。如果遇到这样的问题,建议改成 SAMEORIGIN
|
# deny 将完全不允许页面被嵌套,可能会导致一些异常。如果遇到这样的问题,建议改成 SAMEORIGIN
|
||||||
# https://imququ.com/post/web-security-and-response-header.html#toc-1
|
# https://imququ.com/post/web-security-and-response-header.html#toc-1
|
||||||
add_header X-Frame-Options deny;
|
add_header X-Frame-Options deny;
|
||||||
add_header X-Powered-By eiblog/1.2.1;
|
add_header X-Powered-By eiblog/1.3.0;
|
||||||
add_header X-Content-Type-Options nosniff;
|
add_header X-Content-Type-Options nosniff;
|
||||||
|
|
||||||
proxy_set_header Connection "";
|
proxy_set_header Connection "";
|
||||||
@@ -116,15 +94,14 @@ server {
|
|||||||
add_header Content-Security-Policy "default-src 'none'; script-src 'unsafe-inline' 'unsafe-eval' blob: https:; img-src data: https: https://st.deepzz.com; media-src https://st.deepzz.com; style-src 'unsafe-inline' https:; child-src https:; connect-src 'self' https://translate.googleapis.com; frame-src https://disqus.com https://www.slideshare.net";
|
add_header Content-Security-Policy "default-src 'none'; script-src 'unsafe-inline' 'unsafe-eval' blob: https:; img-src data: https: https://st.deepzz.com; media-src https://st.deepzz.com; style-src 'unsafe-inline' https:; child-src https:; connect-src 'self' https://translate.googleapis.com; frame-src https://disqus.com https://www.slideshare.net";
|
||||||
# 中间证书证书指纹
|
# 中间证书证书指纹
|
||||||
# https://imququ.com/post/http-public-key-pinning.html
|
# https://imququ.com/post/http-public-key-pinning.html
|
||||||
add_header Public-Key-Pins 'pin-sha256="IiSbZ4pMDEyXvtl7Lg8K3FNmJcTAhKUTrB2FQOaAO/s="; pin-sha256="YLh1dUR9y6Kja30RrAn7JKnbQG/uEtLMkBgFF2Fuihg="; max-age=2592000; includeSubDomains';
|
add_header Public-Key-Pins 'pin-sha256="IiSbZ4pMDEyXvtl7Lg8K3FNmJcTAhKUTrB2FQOaAO/s="; pin-sha256="YLh1dUR9y6Kja30RrAn7JKnbQG/uEtLMkBgFF2Fuihg="; max-age=2592000;';
|
||||||
add_header Cache-Control no-cache;
|
add_header Cache-Control no-cache;
|
||||||
add_header X-Via Aliyun.QingDao;
|
add_header X-Via Aliyun.QingDao;
|
||||||
add_header X-XSS-Protection "1; mode=block";
|
add_header X-XSS-Protection "1; mode=block";
|
||||||
|
add_header X-Powered-By eiblog/1.3.0;
|
||||||
|
|
||||||
proxy_ignore_headers Set-Cookie;
|
proxy_ignore_headers Set-Cookie;
|
||||||
|
|
||||||
proxy_hide_header Vary;
|
proxy_hide_header Vary;
|
||||||
proxy_hide_header X-Powered-By;
|
|
||||||
|
|
||||||
proxy_set_header Connection "";
|
proxy_set_header Connection "";
|
||||||
proxy_set_header Host deepzz.com;
|
proxy_set_header Host deepzz.com;
|
||||||
@@ -147,7 +124,7 @@ server {
|
|||||||
|
|
||||||
# letsencrypt file verify
|
# letsencrypt file verify
|
||||||
location ^~ /.well-known/acme-challenge/ {
|
location ^~ /.well-known/acme-challenge/ {
|
||||||
alias /data/letsencrypt/challenges/;
|
alias /data/eiblog/challenges/;
|
||||||
try_files $uri =404;
|
try_files $uri =404;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
406
db.go
406
db.go
@@ -13,9 +13,7 @@ import (
|
|||||||
"github.com/eiblog/blackfriday"
|
"github.com/eiblog/blackfriday"
|
||||||
"github.com/eiblog/eiblog/setting"
|
"github.com/eiblog/eiblog/setting"
|
||||||
"github.com/eiblog/utils/logd"
|
"github.com/eiblog/utils/logd"
|
||||||
db "github.com/eiblog/utils/mgo"
|
"github.com/eiblog/utils/mgo"
|
||||||
"gopkg.in/mgo.v2"
|
|
||||||
"gopkg.in/mgo.v2/bson"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// 数据库及表名
|
// 数据库及表名
|
||||||
@@ -62,44 +60,24 @@ var (
|
|||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
// 数据库加索引
|
// 数据库加索引
|
||||||
ms, c := db.Connect(DB, COLLECTION_ACCOUNT)
|
err := mgo.Index(DB, COLLECTION_ACCOUNT, []string{"username"})
|
||||||
index := mgo.Index{
|
if err != nil {
|
||||||
Key: []string{"username"},
|
|
||||||
Unique: true,
|
|
||||||
DropDups: true,
|
|
||||||
Background: true,
|
|
||||||
Sparse: true,
|
|
||||||
}
|
|
||||||
if err := c.EnsureIndex(index); err != nil {
|
|
||||||
logd.Fatal(err)
|
logd.Fatal(err)
|
||||||
}
|
}
|
||||||
ms.Close()
|
|
||||||
ms, c = db.Connect(DB, COLLECTION_ARTICLE)
|
err = mgo.Index(DB, COLLECTION_ARTICLE, []string{"id"})
|
||||||
index = mgo.Index{
|
if err != nil {
|
||||||
Key: []string{"id"},
|
|
||||||
Unique: true,
|
|
||||||
DropDups: true,
|
|
||||||
Background: true,
|
|
||||||
Sparse: true,
|
|
||||||
}
|
|
||||||
if err := c.EnsureIndex(index); err != nil {
|
|
||||||
logd.Fatal(err)
|
logd.Fatal(err)
|
||||||
}
|
}
|
||||||
index = mgo.Index{
|
|
||||||
Key: []string{"slug"},
|
err = mgo.Index(DB, COLLECTION_ARTICLE, []string{"slug"})
|
||||||
Unique: true,
|
if err != nil {
|
||||||
DropDups: true,
|
|
||||||
Background: true,
|
|
||||||
Sparse: true,
|
|
||||||
}
|
|
||||||
if err := c.EnsureIndex(index); err != nil {
|
|
||||||
logd.Fatal(err)
|
logd.Fatal(err)
|
||||||
}
|
}
|
||||||
ms.Close()
|
|
||||||
// 读取帐号信息
|
// 读取帐号信息
|
||||||
Ei = loadAccount()
|
loadAccount()
|
||||||
// 获取文章数据
|
// 获取文章数据
|
||||||
Ei.Articles = loadArticles()
|
loadArticles()
|
||||||
// 生成markdown文档
|
// 生成markdown文档
|
||||||
go generateMarkdown()
|
go generateMarkdown()
|
||||||
// 启动定时器
|
// 启动定时器
|
||||||
@@ -109,12 +87,13 @@ func init() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 读取或初始化帐号信息
|
// 读取或初始化帐号信息
|
||||||
func loadAccount() (a *Account) {
|
func loadAccount() {
|
||||||
a = &Account{}
|
Ei = &Account{}
|
||||||
err := db.FindOne(DB, COLLECTION_ACCOUNT, bson.M{"username": setting.Conf.Account.Username}, a)
|
err := mgo.FindOne(DB, COLLECTION_ACCOUNT, mgo.M{"username": setting.Conf.Account.Username}, Ei)
|
||||||
// 初始化用户数据
|
// 初始化用户数据
|
||||||
if err == mgo.ErrNotFound {
|
if err == mgo.ErrNotFound {
|
||||||
a = &Account{
|
logd.Printf("Initializing account: %s\n", setting.Conf.Account.Username)
|
||||||
|
Ei = &Account{
|
||||||
Username: setting.Conf.Account.Username,
|
Username: setting.Conf.Account.Username,
|
||||||
Password: EncryptPasswd(setting.Conf.Account.Username, setting.Conf.Account.Password),
|
Password: EncryptPasswd(setting.Conf.Account.Username, setting.Conf.Account.Password),
|
||||||
Email: setting.Conf.Account.Email,
|
Email: setting.Conf.Account.Email,
|
||||||
@@ -122,29 +101,28 @@ func loadAccount() (a *Account) {
|
|||||||
Address: setting.Conf.Account.Address,
|
Address: setting.Conf.Account.Address,
|
||||||
CreateTime: time.Now(),
|
CreateTime: time.Now(),
|
||||||
}
|
}
|
||||||
a.BlogName = setting.Conf.Blogger.BlogName
|
Ei.BlogName = setting.Conf.Blogger.BlogName
|
||||||
a.SubTitle = setting.Conf.Blogger.SubTitle
|
Ei.SubTitle = setting.Conf.Blogger.SubTitle
|
||||||
a.BeiAn = setting.Conf.Blogger.BeiAn
|
Ei.BeiAn = setting.Conf.Blogger.BeiAn
|
||||||
a.BTitle = setting.Conf.Blogger.BTitle
|
Ei.BTitle = setting.Conf.Blogger.BTitle
|
||||||
a.Copyright = setting.Conf.Blogger.Copyright
|
Ei.Copyright = setting.Conf.Blogger.Copyright
|
||||||
err = db.Insert(DB, COLLECTION_ACCOUNT, a)
|
err = mgo.Insert(DB, COLLECTION_ACCOUNT, Ei)
|
||||||
generateTopic()
|
generateTopic()
|
||||||
} else if err != nil {
|
} else if err != nil {
|
||||||
logd.Fatal(err)
|
logd.Fatal(err)
|
||||||
}
|
}
|
||||||
a.CH = make(chan string, 2)
|
Ei.CH = make(chan string, 2)
|
||||||
a.MapArticles = make(map[string]*Article)
|
Ei.MapArticles = make(map[string]*Article)
|
||||||
a.Tags = make(map[string]SortArticles)
|
Ei.Tags = make(map[string]SortArticles)
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func loadArticles() (artcs SortArticles) {
|
func loadArticles() {
|
||||||
err := db.FindAll(DB, COLLECTION_ARTICLE, bson.M{"isdraft": false, "deletetime": bson.M{"$eq": time.Time{}}}, &artcs)
|
err := mgo.FindAll(DB, COLLECTION_ARTICLE, mgo.M{"isdraft": false, "deletetime": mgo.M{"$eq": time.Time{}}}, &Ei.Articles)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logd.Fatal(err)
|
logd.Fatal(err)
|
||||||
}
|
}
|
||||||
sort.Sort(artcs)
|
sort.Sort(Ei.Articles)
|
||||||
for i, v := range artcs {
|
for i, v := range Ei.Articles {
|
||||||
// 渲染文章
|
// 渲染文章
|
||||||
GenerateExcerptAndRender(v)
|
GenerateExcerptAndRender(v)
|
||||||
Ei.MapArticles[v.Slug] = v
|
Ei.MapArticles[v.Slug] = v
|
||||||
@@ -153,18 +131,15 @@ func loadArticles() (artcs SortArticles) {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if i > 0 {
|
if i > 0 {
|
||||||
v.Prev = artcs[i-1]
|
v.Prev = Ei.Articles[i-1]
|
||||||
}
|
}
|
||||||
if artcs[i+1].ID >= setting.Conf.General.StartID {
|
if Ei.Articles[i+1].ID >= setting.Conf.General.StartID {
|
||||||
v.Next = artcs[i+1]
|
v.Next = Ei.Articles[i+1]
|
||||||
}
|
}
|
||||||
ManageTagsArticle(v, false, ADD)
|
upArticle(v, false)
|
||||||
ManageSeriesArticle(v, false, ADD)
|
|
||||||
ManageArchivesArticle(v, false, ADD)
|
|
||||||
}
|
}
|
||||||
Ei.CH <- SERIES_MD
|
Ei.CH <- SERIES_MD
|
||||||
Ei.CH <- ARCHIVE_MD
|
Ei.CH <- ARCHIVE_MD
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// generate series,archive markdown
|
// generate series,archive markdown
|
||||||
@@ -183,7 +158,8 @@ func generateMarkdown() {
|
|||||||
buffer.WriteString("\n\n")
|
buffer.WriteString("\n\n")
|
||||||
for _, artc := range serie.Articles {
|
for _, artc := range serie.Articles {
|
||||||
//eg. * [标题一](/post/hello-world.html) <span class="date">(Man 02, 2006)</span>
|
//eg. * [标题一](/post/hello-world.html) <span class="date">(Man 02, 2006)</span>
|
||||||
buffer.WriteString("* [" + artc.Title + "](/post/" + artc.Slug + ".html) <span class=\"date\">(" + artc.CreateTime.Format("Jan 02, 2006") + ")</span>\n")
|
buffer.WriteString("* [" + artc.Title + "](/post/" + artc.Slug +
|
||||||
|
".html) <span class=\"date\">(" + artc.CreateTime.Format("Jan 02, 2006") + ")</span>\n")
|
||||||
}
|
}
|
||||||
buffer.WriteByte('\n')
|
buffer.WriteByte('\n')
|
||||||
}
|
}
|
||||||
@@ -191,15 +167,31 @@ func generateMarkdown() {
|
|||||||
case ARCHIVE_MD:
|
case ARCHIVE_MD:
|
||||||
sort.Sort(Ei.Archives)
|
sort.Sort(Ei.Archives)
|
||||||
var buffer bytes.Buffer
|
var buffer bytes.Buffer
|
||||||
buffer.WriteString(Ei.ArchivesSay)
|
buffer.WriteString(Ei.ArchivesSay + "\n")
|
||||||
buffer.WriteString("\n\n")
|
|
||||||
|
var (
|
||||||
|
currentYear string
|
||||||
|
gt12Month = len(Ei.Archives) > 12
|
||||||
|
)
|
||||||
for _, archive := range Ei.Archives {
|
for _, archive := range Ei.Archives {
|
||||||
buffer.WriteString(fmt.Sprintf("### %s", archive.Time.Format("2006年01月")))
|
if gt12Month {
|
||||||
buffer.WriteString("\n\n")
|
year := archive.Time.Format("2006 年")
|
||||||
for _, artc := range archive.Articles {
|
if currentYear != year {
|
||||||
buffer.WriteString("* [" + artc.Title + "](/post/" + artc.Slug + ".html) <span class=\"date\">(" + artc.CreateTime.Format("Jan 02, 2006") + ")</span>\n")
|
currentYear = year
|
||||||
|
buffer.WriteString(fmt.Sprintf("\n### %s\n\n", archive.Time.Format("2006 年")))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
buffer.WriteString(fmt.Sprintf("\n### %s\n\n", archive.Time.Format("2006年1月")))
|
||||||
|
}
|
||||||
|
for i, artc := range archive.Articles {
|
||||||
|
if i == 0 && gt12Month {
|
||||||
|
buffer.WriteString("* *[" + artc.Title + "](/post/" + artc.Slug +
|
||||||
|
".html) <span class=\"date\">(" + artc.CreateTime.Format("Jan 02, 2006") + ")</span>*\n")
|
||||||
|
} else {
|
||||||
|
buffer.WriteString("* [" + artc.Title + "](/post/" + artc.Slug +
|
||||||
|
".html) <span class=\"date\">(" + artc.CreateTime.Format("Jan 02, 2006") + ")</span>\n")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
buffer.WriteByte('\n')
|
|
||||||
}
|
}
|
||||||
Ei.PageArchives = string(renderPage(buffer.Bytes()))
|
Ei.PageArchives = string(renderPage(buffer.Bytes()))
|
||||||
}
|
}
|
||||||
@@ -209,26 +201,29 @@ func generateMarkdown() {
|
|||||||
// init account: generate blogroll and about page
|
// init account: generate blogroll and about page
|
||||||
func generateTopic() {
|
func generateTopic() {
|
||||||
about := &Article{
|
about := &Article{
|
||||||
ID: db.NextVal(DB, COUNTER_ARTICLE),
|
ID: mgo.NextVal(DB, COUNTER_ARTICLE),
|
||||||
Author: setting.Conf.Account.Username,
|
Author: setting.Conf.Account.Username,
|
||||||
Title: "关于",
|
Title: "关于",
|
||||||
Slug: "about",
|
Slug: "about",
|
||||||
CreateTime: time.Now(),
|
CreateTime: time.Time{},
|
||||||
UpdateTime: time.Time{},
|
UpdateTime: time.Time{},
|
||||||
}
|
}
|
||||||
|
// 推送到 disqus
|
||||||
|
go func() { ThreadCreate(about) }()
|
||||||
|
|
||||||
blogroll := &Article{
|
blogroll := &Article{
|
||||||
ID: db.NextVal(DB, COUNTER_ARTICLE),
|
ID: mgo.NextVal(DB, COUNTER_ARTICLE),
|
||||||
Author: setting.Conf.Account.Username,
|
Author: setting.Conf.Account.Username,
|
||||||
Title: "友情链接",
|
Title: "友情链接",
|
||||||
Slug: "blogroll",
|
Slug: "blogroll",
|
||||||
UpdateTime: time.Now(),
|
|
||||||
CreateTime: time.Time{},
|
CreateTime: time.Time{},
|
||||||
|
UpdateTime: time.Time{},
|
||||||
}
|
}
|
||||||
err := db.Insert(DB, COLLECTION_ARTICLE, blogroll)
|
err := mgo.Insert(DB, COLLECTION_ARTICLE, blogroll)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logd.Fatal(err)
|
logd.Fatal(err)
|
||||||
}
|
}
|
||||||
err = db.Insert(DB, COLLECTION_ARTICLE, about)
|
err = mgo.Insert(DB, COLLECTION_ARTICLE, about)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logd.Fatal(err)
|
logd.Fatal(err)
|
||||||
}
|
}
|
||||||
@@ -273,94 +268,6 @@ func PageList(p, n int) (prev int, next int, artcs []*Article) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func ManageTagsArticle(artc *Article, s bool, do string) {
|
|
||||||
switch do {
|
|
||||||
case ADD:
|
|
||||||
for _, tag := range artc.Tags {
|
|
||||||
Ei.Tags[tag] = append(Ei.Tags[tag], artc)
|
|
||||||
if s {
|
|
||||||
sort.Sort(Ei.Tags[tag])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
case DELETE:
|
|
||||||
for _, tag := range artc.Tags {
|
|
||||||
for i, v := range Ei.Tags[tag] {
|
|
||||||
if v == artc {
|
|
||||||
Ei.Tags[tag] = append(Ei.Tags[tag][0:i], Ei.Tags[tag][i+1:]...)
|
|
||||||
if len(Ei.Tags[tag]) == 0 {
|
|
||||||
delete(Ei.Tags, tag)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func ManageSeriesArticle(artc *Article, s bool, do string) {
|
|
||||||
switch do {
|
|
||||||
case ADD:
|
|
||||||
for i, serie := range Ei.Series {
|
|
||||||
if serie.ID == artc.SerieID {
|
|
||||||
Ei.Series[i].Articles = append(Ei.Series[i].Articles, artc)
|
|
||||||
if s {
|
|
||||||
sort.Sort(Ei.Series[i].Articles)
|
|
||||||
Ei.CH <- SERIES_MD
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
case DELETE:
|
|
||||||
for i, serie := range Ei.Series {
|
|
||||||
if serie.ID == artc.SerieID {
|
|
||||||
for j, v := range serie.Articles {
|
|
||||||
if v == artc {
|
|
||||||
Ei.Series[i].Articles = append(Ei.Series[i].Articles[0:j], Ei.Series[i].Articles[j+1:]...)
|
|
||||||
Ei.CH <- SERIES_MD
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func ManageArchivesArticle(artc *Article, s bool, do string) {
|
|
||||||
switch do {
|
|
||||||
case ADD:
|
|
||||||
add := false
|
|
||||||
y, m, _ := artc.CreateTime.Date()
|
|
||||||
for i, archive := range Ei.Archives {
|
|
||||||
ay, am, _ := archive.Time.Date()
|
|
||||||
if y == ay && m == am {
|
|
||||||
add = true
|
|
||||||
Ei.Archives[i].Articles = append(Ei.Archives[i].Articles, artc)
|
|
||||||
if s {
|
|
||||||
sort.Sort(Ei.Archives[i].Articles)
|
|
||||||
Ei.CH <- ARCHIVE_MD
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if !add {
|
|
||||||
Ei.Archives = append(Ei.Archives, &Archive{Time: artc.CreateTime, Articles: SortArticles{artc}})
|
|
||||||
}
|
|
||||||
case DELETE:
|
|
||||||
for i, archive := range Ei.Archives {
|
|
||||||
ay, am, _ := archive.Time.Date()
|
|
||||||
if y, m, _ := artc.CreateTime.Date(); ay == y && am == m {
|
|
||||||
for j, v := range archive.Articles {
|
|
||||||
if v == artc {
|
|
||||||
Ei.Archives[i].Articles = append(Ei.Archives[i].Articles[0:j], Ei.Archives[i].Articles[j+1:]...)
|
|
||||||
Ei.CH <- ARCHIVE_MD
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 渲染markdown操作和截取摘要操作
|
// 渲染markdown操作和截取摘要操作
|
||||||
var reg = regexp.MustCompile(setting.Conf.General.Identifier)
|
var reg = regexp.MustCompile(setting.Conf.General.Identifier)
|
||||||
|
|
||||||
@@ -374,6 +281,7 @@ func GenerateExcerptAndRender(artc *Article) {
|
|||||||
artc.Content = artc.Content[index:]
|
artc.Content = artc.Content[index:]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 查找目录
|
||||||
content := renderPage([]byte(artc.Content))
|
content := renderPage([]byte(artc.Content))
|
||||||
index := regH.FindIndex(content)
|
index := regH.FindIndex(content)
|
||||||
if index != nil {
|
if index != nil {
|
||||||
@@ -397,44 +305,151 @@ func GenerateExcerptAndRender(artc *Article) {
|
|||||||
|
|
||||||
// 读取草稿箱
|
// 读取草稿箱
|
||||||
func LoadDraft() (artcs SortArticles, err error) {
|
func LoadDraft() (artcs SortArticles, err error) {
|
||||||
err = db.FindAll(DB, COLLECTION_ARTICLE, bson.M{"isdraft": true}, &artcs)
|
err = mgo.FindAll(DB, COLLECTION_ARTICLE, mgo.M{"isdraft": true}, &artcs)
|
||||||
sort.Sort(artcs)
|
sort.Sort(artcs)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// 读取回收箱
|
// 读取回收箱
|
||||||
func LoadTrash() (artcs SortArticles, err error) {
|
func LoadTrash() (artcs SortArticles, err error) {
|
||||||
err = db.FindAll(DB, COLLECTION_ARTICLE, bson.M{"deletetime": bson.M{"$ne": time.Time{}}}, &artcs)
|
err = mgo.FindAll(DB, COLLECTION_ARTICLE, mgo.M{"deletetime": mgo.M{"$ne": time.Time{}}}, &artcs)
|
||||||
sort.Sort(artcs)
|
sort.Sort(artcs)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 添加文章到tag、serie、archive
|
||||||
|
func upArticle(artc *Article, needSort bool) {
|
||||||
|
// tag
|
||||||
|
for _, tag := range artc.Tags {
|
||||||
|
Ei.Tags[tag] = append(Ei.Tags[tag], artc)
|
||||||
|
if needSort {
|
||||||
|
sort.Sort(Ei.Tags[tag])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// serie
|
||||||
|
for i, serie := range Ei.Series {
|
||||||
|
if serie.ID == artc.SerieID {
|
||||||
|
Ei.Series[i].Articles = append(Ei.Series[i].Articles, artc)
|
||||||
|
if needSort {
|
||||||
|
sort.Sort(Ei.Series[i].Articles)
|
||||||
|
Ei.CH <- SERIES_MD
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// archive
|
||||||
|
y, m, _ := artc.CreateTime.Date()
|
||||||
|
for i, archive := range Ei.Archives {
|
||||||
|
if ay, am, _ := archive.Time.Date(); y == ay && m == am {
|
||||||
|
Ei.Archives[i].Articles = append(Ei.Archives[i].Articles, artc)
|
||||||
|
if needSort {
|
||||||
|
sort.Sort(Ei.Archives[i].Articles)
|
||||||
|
Ei.CH <- ARCHIVE_MD
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ei.Archives = append(Ei.Archives, &Archive{Time: artc.CreateTime,
|
||||||
|
Articles: SortArticles{artc}})
|
||||||
|
if needSort {
|
||||||
|
Ei.CH <- ARCHIVE_MD
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 删除文章从tag、serie、archive
|
||||||
|
func dropArticle(artc *Article) {
|
||||||
|
// tag
|
||||||
|
for _, tag := range artc.Tags {
|
||||||
|
for i, v := range Ei.Tags[tag] {
|
||||||
|
if v == artc {
|
||||||
|
Ei.Tags[tag] = append(Ei.Tags[tag][0:i], Ei.Tags[tag][i+1:]...)
|
||||||
|
if len(Ei.Tags[tag]) == 0 {
|
||||||
|
delete(Ei.Tags, tag)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// serie
|
||||||
|
for i, serie := range Ei.Series {
|
||||||
|
if serie.ID == artc.SerieID {
|
||||||
|
for j, v := range serie.Articles {
|
||||||
|
if v == artc {
|
||||||
|
Ei.Series[i].Articles = append(Ei.Series[i].Articles[0:j],
|
||||||
|
Ei.Series[i].Articles[j+1:]...)
|
||||||
|
Ei.CH <- SERIES_MD
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// archive
|
||||||
|
for i, archive := range Ei.Archives {
|
||||||
|
ay, am, _ := archive.Time.Date()
|
||||||
|
if y, m, _ := artc.CreateTime.Date(); ay == y && am == m {
|
||||||
|
for j, v := range archive.Articles {
|
||||||
|
if v == artc {
|
||||||
|
Ei.Archives[i].Articles = append(Ei.Archives[i].Articles[0:j],
|
||||||
|
Ei.Archives[i].Articles[j+1:]...)
|
||||||
|
if len(Ei.Archives[i].Articles) == 0 {
|
||||||
|
Ei.Archives = append(Ei.Archives[:i], Ei.Archives[i+1:]...)
|
||||||
|
}
|
||||||
|
Ei.CH <- ARCHIVE_MD
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 替换文章
|
||||||
|
func ReplaceArticle(oldArtc *Article, newArtc *Article) {
|
||||||
|
if oldArtc != nil {
|
||||||
|
i, artc := GetArticle(oldArtc.ID)
|
||||||
|
DelFromLinkedList(artc)
|
||||||
|
Ei.Articles = append(Ei.Articles[:i], Ei.Articles[i+1:]...)
|
||||||
|
delete(Ei.MapArticles, artc.Slug)
|
||||||
|
|
||||||
|
dropArticle(oldArtc)
|
||||||
|
}
|
||||||
|
|
||||||
|
Ei.MapArticles[newArtc.Slug] = newArtc
|
||||||
|
Ei.Articles = append(Ei.Articles, newArtc)
|
||||||
|
sort.Sort(Ei.Articles)
|
||||||
|
|
||||||
|
GenerateExcerptAndRender(newArtc)
|
||||||
|
AddToLinkedList(newArtc.ID)
|
||||||
|
|
||||||
|
upArticle(newArtc, true)
|
||||||
|
}
|
||||||
|
|
||||||
// 添加文章
|
// 添加文章
|
||||||
func AddArticle(artc *Article) error {
|
func AddArticle(artc *Article) error {
|
||||||
// 分配ID, 占位至起始id
|
// 分配ID, 占位至起始id
|
||||||
for {
|
for {
|
||||||
if id := db.NextVal(DB, COUNTER_ARTICLE); id < setting.Conf.General.StartID {
|
if id := mgo.NextVal(DB, COUNTER_ARTICLE); id < setting.Conf.General.StartID {
|
||||||
continue
|
continue
|
||||||
} else {
|
} else {
|
||||||
artc.ID = id
|
artc.ID = id
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
err := mgo.Insert(DB, COLLECTION_ARTICLE, artc)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// 正式发布文章
|
||||||
if !artc.IsDraft {
|
if !artc.IsDraft {
|
||||||
defer GenerateExcerptAndRender(artc)
|
defer GenerateExcerptAndRender(artc)
|
||||||
Ei.MapArticles[artc.Slug] = artc
|
Ei.MapArticles[artc.Slug] = artc
|
||||||
Ei.Articles = append([]*Article{artc}, Ei.Articles...)
|
Ei.Articles = append([]*Article{artc}, Ei.Articles...)
|
||||||
sort.Sort(Ei.Articles)
|
sort.Sort(Ei.Articles)
|
||||||
AddToLinkedList(artc.ID)
|
AddToLinkedList(artc.ID)
|
||||||
ManageTagsArticle(artc, true, ADD)
|
|
||||||
ManageSeriesArticle(artc, true, ADD)
|
upArticle(artc, true)
|
||||||
ManageArchivesArticle(artc, true, ADD)
|
|
||||||
Ei.CH <- ARCHIVE_MD
|
|
||||||
if artc.SerieID > 0 {
|
|
||||||
Ei.CH <- SERIES_MD
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return db.Insert(DB, COLLECTION_ARTICLE, artc)
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// 删除文章,移入回收箱
|
// 删除文章,移入回收箱
|
||||||
@@ -446,20 +461,17 @@ func DelArticles(ids ...int32) error {
|
|||||||
DelFromLinkedList(artc)
|
DelFromLinkedList(artc)
|
||||||
Ei.Articles = append(Ei.Articles[:i], Ei.Articles[i+1:]...)
|
Ei.Articles = append(Ei.Articles[:i], Ei.Articles[i+1:]...)
|
||||||
delete(Ei.MapArticles, artc.Slug)
|
delete(Ei.MapArticles, artc.Slug)
|
||||||
ManageTagsArticle(artc, false, DELETE)
|
|
||||||
ManageSeriesArticle(artc, false, DELETE)
|
err := UpdateArticle(mgo.M{"id": id}, mgo.M{"$set": mgo.M{"deletetime": time.Now()}})
|
||||||
ManageArchivesArticle(artc, false, DELETE)
|
|
||||||
err := UpdateArticle(bson.M{"id": id}, bson.M{"$set": bson.M{"deletetime": time.Now()}})
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
artc = nil
|
dropArticle(artc)
|
||||||
}
|
}
|
||||||
Ei.CH <- ARCHIVE_MD
|
|
||||||
Ei.CH <- SERIES_MD
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 从链表里删除文章
|
||||||
func DelFromLinkedList(artc *Article) {
|
func DelFromLinkedList(artc *Article) {
|
||||||
if artc.Prev == nil && artc.Next != nil {
|
if artc.Prev == nil && artc.Next != nil {
|
||||||
artc.Next.Prev = nil
|
artc.Next.Prev = nil
|
||||||
@@ -471,6 +483,7 @@ func DelFromLinkedList(artc *Article) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 将文章添加到链表
|
||||||
func AddToLinkedList(id int32) {
|
func AddToLinkedList(id int32) {
|
||||||
i, artc := GetArticle(id)
|
i, artc := GetArticle(id)
|
||||||
if i == 0 && Ei.Articles[i+1].ID >= setting.Conf.General.StartID {
|
if i == 0 && Ei.Articles[i+1].ID >= setting.Conf.General.StartID {
|
||||||
@@ -501,34 +514,36 @@ func timer() {
|
|||||||
delT := time.NewTicker(time.Duration(setting.Conf.General.Clean) * time.Hour)
|
delT := time.NewTicker(time.Duration(setting.Conf.General.Clean) * time.Hour)
|
||||||
for {
|
for {
|
||||||
<-delT.C
|
<-delT.C
|
||||||
db.Remove(DB, COLLECTION_ARTICLE, bson.M{"deletetime": bson.M{"$gt": time.Time{}, "$lt": time.Now().Add(time.Duration(setting.Conf.General.Trash) * time.Hour)}})
|
mgo.Remove(DB, COLLECTION_ARTICLE, mgo.M{"deletetime": mgo.M{"$gt": time.Time{},
|
||||||
|
"$lt": time.Now().Add(time.Duration(setting.Conf.General.Trash) * time.Hour)}})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 操作帐号字段
|
// 操作帐号字段
|
||||||
func UpdateAccountField(M bson.M) error {
|
func UpdateAccountField(M mgo.M) error {
|
||||||
return db.Update(DB, COLLECTION_ACCOUNT, bson.M{"username": Ei.Username}, M)
|
return mgo.Update(DB, COLLECTION_ACCOUNT, mgo.M{"username": Ei.Username}, M)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 删除草稿箱或回收箱,永久删除
|
// 删除草稿箱或回收箱,永久删除
|
||||||
func RemoveArticle(id int32) error {
|
func RemoveArticle(id int32) error {
|
||||||
return db.Remove(DB, COLLECTION_ARTICLE, bson.M{"id": id})
|
return mgo.Remove(DB, COLLECTION_ARTICLE, mgo.M{"id": id})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 恢复删除文章到草稿箱
|
// 恢复删除文章到草稿箱
|
||||||
func RecoverArticle(id int32) error {
|
func RecoverArticle(id int32) error {
|
||||||
return db.Update(DB, COLLECTION_ARTICLE, bson.M{"id": id}, bson.M{"$set": bson.M{"deletetime": time.Time{}, "isdraft": true}})
|
return mgo.Update(DB, COLLECTION_ARTICLE, mgo.M{"id": id},
|
||||||
|
mgo.M{"$set": mgo.M{"deletetime": time.Time{}, "isdraft": true}})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 更新文章
|
// 更新文章
|
||||||
func UpdateArticle(query, update interface{}) error {
|
func UpdateArticle(query, update interface{}) error {
|
||||||
return db.Update(DB, COLLECTION_ARTICLE, query, update)
|
return mgo.Update(DB, COLLECTION_ARTICLE, query, update)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 编辑文档
|
// 编辑文档
|
||||||
func QueryArticle(id int32) *Article {
|
func QueryArticle(id int32) *Article {
|
||||||
artc := &Article{}
|
artc := &Article{}
|
||||||
if err := db.FindOne(DB, COLLECTION_ARTICLE, bson.M{"id": id}, artc); err != nil {
|
if err := mgo.FindOne(DB, COLLECTION_ARTICLE, mgo.M{"id": id}, artc); err != nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
return artc
|
return artc
|
||||||
@@ -536,17 +551,18 @@ func QueryArticle(id int32) *Article {
|
|||||||
|
|
||||||
// 添加专题
|
// 添加专题
|
||||||
func AddSerie(name, slug, desc string) error {
|
func AddSerie(name, slug, desc string) error {
|
||||||
serie := &Serie{db.NextVal(DB, COUNTER_SERIE), name, slug, desc, time.Now(), nil}
|
serie := &Serie{mgo.NextVal(DB, COUNTER_SERIE), name, slug, desc, time.Now(), nil}
|
||||||
Ei.Series = append(Ei.Series, serie)
|
Ei.Series = append(Ei.Series, serie)
|
||||||
sort.Sort(Ei.Series)
|
sort.Sort(Ei.Series)
|
||||||
Ei.CH <- SERIES_MD
|
Ei.CH <- SERIES_MD
|
||||||
return UpdateAccountField(bson.M{"$addToSet": bson.M{"blogger.series": serie}})
|
return UpdateAccountField(mgo.M{"$addToSet": mgo.M{"blogger.series": serie}})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 更新专题
|
// 更新专题
|
||||||
func UpdateSerie(serie *Serie) error {
|
func UpdateSerie(serie *Serie) error {
|
||||||
Ei.CH <- SERIES_MD
|
Ei.CH <- SERIES_MD
|
||||||
return db.Update(DB, COLLECTION_ACCOUNT, bson.M{"username": Ei.Username, "blogger.series.id": serie.ID}, bson.M{"$set": bson.M{"blogger.series.$": serie}})
|
return mgo.Update(DB, COLLECTION_ACCOUNT, mgo.M{"username": Ei.Username,
|
||||||
|
"blogger.series.id": serie.ID}, mgo.M{"$set": mgo.M{"blogger.series.$": serie}})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 删除专题
|
// 删除专题
|
||||||
@@ -556,7 +572,7 @@ func DelSerie(id int32) error {
|
|||||||
if len(serie.Articles) > 0 {
|
if len(serie.Articles) > 0 {
|
||||||
return fmt.Errorf("请删除该专题下的所有文章")
|
return fmt.Errorf("请删除该专题下的所有文章")
|
||||||
}
|
}
|
||||||
err := UpdateAccountField(bson.M{"$pull": bson.M{"blogger.series": bson.M{"id": id}}})
|
err := UpdateAccountField(mgo.M{"$pull": mgo.M{"blogger.series": mgo.M{"id": id}}})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -580,24 +596,24 @@ func QuerySerie(id int32) *Serie {
|
|||||||
|
|
||||||
// 后台分页
|
// 后台分页
|
||||||
func PageListBack(se int, kw string, draft, del bool, p, n int) (max int, artcs []*Article) {
|
func PageListBack(se int, kw string, draft, del bool, p, n int) (max int, artcs []*Article) {
|
||||||
M := bson.M{}
|
M := mgo.M{}
|
||||||
if draft {
|
if draft {
|
||||||
M["isdraft"] = true
|
M["isdraft"] = true
|
||||||
} else if del {
|
} else if del {
|
||||||
M["deletetime"] = bson.M{"$ne": time.Time{}}
|
M["deletetime"] = mgo.M{"$ne": time.Time{}}
|
||||||
} else {
|
} else {
|
||||||
M["isdraft"] = false
|
M["isdraft"] = false
|
||||||
M["deletetime"] = bson.M{"$eq": time.Time{}}
|
M["deletetime"] = mgo.M{"$eq": time.Time{}}
|
||||||
if se > 0 {
|
if se > 0 {
|
||||||
M["serieid"] = se
|
M["serieid"] = se
|
||||||
}
|
}
|
||||||
if kw != "" {
|
if kw != "" {
|
||||||
M["title"] = bson.M{"$regex": kw, "$options": "$i"}
|
M["title"] = mgo.M{"$regex": kw, "$options": "$i"}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ms, c := db.Connect(DB, COLLECTION_ARTICLE)
|
ms, c := mgo.Connect(DB, COLLECTION_ARTICLE)
|
||||||
defer ms.Close()
|
defer ms.Close()
|
||||||
err := c.Find(M).Select(bson.M{"content": 0}).Sort("-createtime").Limit(n).Skip((p - 1) * n).All(&artcs)
|
err := c.Find(M).Select(mgo.M{"content": 0}).Sort("-createtime").Limit(n).Skip((p - 1) * n).All(&artcs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logd.Error(err)
|
logd.Error(err)
|
||||||
}
|
}
|
||||||
|
|||||||
249
disqus.go
249
disqus.go
@@ -8,6 +8,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"net/url"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@@ -17,7 +18,14 @@ import (
|
|||||||
|
|
||||||
var ErrDisqusConfig = errors.New("disqus config incorrect")
|
var ErrDisqusConfig = errors.New("disqus config incorrect")
|
||||||
|
|
||||||
type result struct {
|
func correctDisqusConfig() bool {
|
||||||
|
return setting.Conf.Disqus.PostsCount != "" &&
|
||||||
|
setting.Conf.Disqus.PublicKey != "" &&
|
||||||
|
setting.Conf.Disqus.ShortName != ""
|
||||||
|
}
|
||||||
|
|
||||||
|
// 定时获取所有文章评论数量
|
||||||
|
type postsCountResp struct {
|
||||||
Code int
|
Code int
|
||||||
Response []struct {
|
Response []struct {
|
||||||
Id string
|
Id string
|
||||||
@@ -26,11 +34,8 @@ type result struct {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 定时获取所有文章评论数量
|
|
||||||
func PostsCount() error {
|
func PostsCount() error {
|
||||||
if setting.Conf.Disqus.PostsCount == "" ||
|
if !correctDisqusConfig() {
|
||||||
setting.Conf.Disqus.PublicKey == "" ||
|
|
||||||
setting.Conf.Disqus.ShortName == "" {
|
|
||||||
return ErrDisqusConfig
|
return ErrDisqusConfig
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -41,20 +46,19 @@ func PostsCount() error {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
baseUrl := setting.Conf.Disqus.PostsCount +
|
vals := url.Values{}
|
||||||
"?api_key=" + setting.Conf.Disqus.PublicKey +
|
vals.Set("api_key", setting.Conf.Disqus.PublicKey)
|
||||||
"&forum=" + setting.Conf.Disqus.ShortName + "&"
|
vals.Set("forum", setting.Conf.Disqus.ShortName)
|
||||||
|
|
||||||
var count, index int
|
var count, index int
|
||||||
for index < len(Ei.Articles) {
|
for index < len(Ei.Articles) {
|
||||||
var threads []string
|
|
||||||
for ; index < len(Ei.Articles) && count < 50; index++ {
|
for ; index < len(Ei.Articles) && count < 50; index++ {
|
||||||
artc := Ei.Articles[index]
|
artc := Ei.Articles[index]
|
||||||
threads = append(threads, fmt.Sprintf("thread:ident=post-%s", artc.Slug))
|
vals.Add("thread:ident", "post-"+artc.Slug)
|
||||||
count++
|
count++
|
||||||
}
|
}
|
||||||
count = 0
|
count = 0
|
||||||
url := baseUrl + strings.Join(threads, "&")
|
resp, err := http.Get(setting.Conf.Disqus.PostsCount + "?" + vals.Encode())
|
||||||
resp, err := http.Get(url)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -68,12 +72,12 @@ func PostsCount() error {
|
|||||||
return errors.New(string(b))
|
return errors.New(string(b))
|
||||||
}
|
}
|
||||||
|
|
||||||
rst := result{}
|
result := &postsCountResp{}
|
||||||
err = json.Unmarshal(b, &rst)
|
err = json.Unmarshal(b, result)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
for _, v := range rst.Response {
|
for _, v := range result.Response {
|
||||||
i := strings.Index(v.Identifiers[0], "-")
|
i := strings.Index(v.Identifiers[0], "-")
|
||||||
artc := Ei.MapArticles[v.Identifiers[0][i+1:]]
|
artc := Ei.MapArticles[v.Identifiers[0][i+1:]]
|
||||||
if artc != nil {
|
if artc != nil {
|
||||||
@@ -86,43 +90,50 @@ func PostsCount() error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
type postsList struct {
|
// 获取文章评论列表
|
||||||
|
type postsListResp struct {
|
||||||
Cursor struct {
|
Cursor struct {
|
||||||
HasNext bool
|
HasNext bool
|
||||||
Next string
|
Next string
|
||||||
}
|
}
|
||||||
Code int
|
Code int
|
||||||
Response []struct {
|
Response []postDetail
|
||||||
Parent int
|
|
||||||
Id string
|
|
||||||
CreatedAt string
|
|
||||||
Message string
|
|
||||||
Author struct {
|
|
||||||
Name string
|
|
||||||
ProfileUrl string
|
|
||||||
Avatar struct {
|
|
||||||
Cache string
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Thread string
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取文章评论列表
|
type postDetail struct {
|
||||||
func PostsList(slug, cursor string) (*postsList, error) {
|
Parent int
|
||||||
if setting.Conf.Disqus.PostsList == "" ||
|
Id string
|
||||||
setting.Conf.Disqus.PublicKey == "" ||
|
CreatedAt string
|
||||||
setting.Conf.Disqus.ShortName == "" {
|
Message string
|
||||||
|
IsDeleted bool
|
||||||
|
Author struct {
|
||||||
|
Name string
|
||||||
|
ProfileUrl string
|
||||||
|
Avatar struct {
|
||||||
|
Cache string
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Thread string
|
||||||
|
}
|
||||||
|
|
||||||
|
func PostsList(slug, cursor string) (*postsListResp, error) {
|
||||||
|
if !correctDisqusConfig() {
|
||||||
return nil, ErrDisqusConfig
|
return nil, ErrDisqusConfig
|
||||||
}
|
}
|
||||||
url := setting.Conf.Disqus.PostsList + "?limit=50&api_key=" +
|
|
||||||
setting.Conf.Disqus.PublicKey + "&forum=" + setting.Conf.Disqus.ShortName +
|
vals := url.Values{}
|
||||||
"&cursor=" + cursor + "&thread:ident=post-" + slug
|
vals.Set("api_key", setting.Conf.Disqus.PublicKey)
|
||||||
resp, err := http.Get(url)
|
vals.Set("forum", setting.Conf.Disqus.ShortName)
|
||||||
|
vals.Set("thread:ident", "post-"+slug)
|
||||||
|
vals.Set("cursor", cursor)
|
||||||
|
vals.Set("limit", "50")
|
||||||
|
|
||||||
|
resp, err := http.Get(setting.Conf.Disqus.PostsList + "?" + vals.Encode())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
defer resp.Body.Close()
|
defer resp.Body.Close()
|
||||||
|
|
||||||
b, err := ioutil.ReadAll(resp.Body)
|
b, err := ioutil.ReadAll(resp.Body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -130,90 +141,91 @@ func PostsList(slug, cursor string) (*postsList, error) {
|
|||||||
if resp.StatusCode != http.StatusOK {
|
if resp.StatusCode != http.StatusOK {
|
||||||
return nil, errors.New(string(b))
|
return nil, errors.New(string(b))
|
||||||
}
|
}
|
||||||
pl := &postsList{}
|
|
||||||
err = json.Unmarshal(b, pl)
|
result := &postsListResp{}
|
||||||
|
err = json.Unmarshal(b, result)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return pl, nil
|
return result, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
type PostCreate struct {
|
type PostComment struct {
|
||||||
Message string `json:"message"`
|
Message string
|
||||||
Parent string `json:"parent"`
|
Parent string
|
||||||
Thread string `json:"thread"`
|
Thread string
|
||||||
AuthorEmail string `json:"author_email"`
|
AuthorEmail string
|
||||||
AuthorName string `json:"autor_name"`
|
AuthorName string
|
||||||
IpAddress string `json:"ip_address"`
|
IpAddress string
|
||||||
Identifier string `json:"identifier"`
|
Identifier string
|
||||||
UserAgent string `json:"user_agent"`
|
UserAgent string
|
||||||
}
|
}
|
||||||
|
|
||||||
type PostResponse struct {
|
type postCreateResp struct {
|
||||||
Code int `json:"code"`
|
Code int
|
||||||
Response struct {
|
Response postDetail
|
||||||
Id string `json:"id"`
|
|
||||||
} `json:"response"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 评论文章
|
// 评论文章
|
||||||
func PostComment(pc *PostCreate) (string, error) {
|
func PostCreate(pc *PostComment) (*postCreateResp, error) {
|
||||||
if setting.Conf.Disqus.PostsList == "" ||
|
if !correctDisqusConfig() {
|
||||||
setting.Conf.Disqus.PublicKey == "" ||
|
return nil, ErrDisqusConfig
|
||||||
setting.Conf.Disqus.ShortName == "" {
|
|
||||||
return "", ErrDisqusConfig
|
|
||||||
}
|
}
|
||||||
url := setting.Conf.Disqus.PostCreate +
|
|
||||||
"?api_key=E8Uh5l5fHZ6gD8U3KycjAIAk46f68Zw7C6eW8WSjZvCLXebZ7p0r1yrYDrLilk2F" +
|
|
||||||
"&message=" + pc.Message + "&parent=" + pc.Parent +
|
|
||||||
"&thread=" + pc.Thread + "&author_email=" + pc.AuthorEmail +
|
|
||||||
"&author_name=" + pc.AuthorName
|
|
||||||
|
|
||||||
request, err := http.NewRequest("POST", url, nil)
|
vals := url.Values{}
|
||||||
|
vals.Set("api_key", "E8Uh5l5fHZ6gD8U3KycjAIAk46f68Zw7C6eW8WSjZvCLXebZ7p0r1yrYDrLilk2F")
|
||||||
|
vals.Set("message", pc.Message)
|
||||||
|
vals.Set("parent", pc.Parent)
|
||||||
|
vals.Set("thread", pc.Thread)
|
||||||
|
vals.Set("author_email", pc.AuthorEmail)
|
||||||
|
vals.Set("author_name", pc.AuthorName)
|
||||||
|
// vals.Set("state", "approved")
|
||||||
|
|
||||||
|
request, err := http.NewRequest("POST", setting.Conf.Disqus.PostCreate, strings.NewReader(vals.Encode()))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return nil, err
|
||||||
}
|
}
|
||||||
request.Header.Set("Referer", "https://disqus.com")
|
request.Header.Set("Referer", "https://disqus.com")
|
||||||
resp, err := http.DefaultClient.Do(request)
|
resp, err := http.DefaultClient.Do(request)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return nil, err
|
||||||
}
|
}
|
||||||
defer resp.Body.Close()
|
defer resp.Body.Close()
|
||||||
b, err := ioutil.ReadAll(resp.Body)
|
b, err := ioutil.ReadAll(resp.Body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return nil, err
|
||||||
}
|
}
|
||||||
if resp.StatusCode != http.StatusOK {
|
|
||||||
return "", errors.New(string(b))
|
|
||||||
}
|
|
||||||
pr := &PostResponse{}
|
|
||||||
err = json.Unmarshal(b, pr)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
return pr.Response.Id, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
type ApprovedResponse struct {
|
if resp.StatusCode != http.StatusOK {
|
||||||
Code int `json:"code"`
|
return nil, errors.New(string(b))
|
||||||
Response []struct {
|
}
|
||||||
Id string `json:"id"`
|
result := &postCreateResp{}
|
||||||
} `json:"response"`
|
err = json.Unmarshal(b, result)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return result, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// 批准评论通过
|
// 批准评论通过
|
||||||
|
type approvedResp struct {
|
||||||
|
Code int
|
||||||
|
Response []struct {
|
||||||
|
Id string
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func PostApprove(post string) error {
|
func PostApprove(post string) error {
|
||||||
if setting.Conf.Disqus.PostsList == "" ||
|
if !correctDisqusConfig() {
|
||||||
setting.Conf.Disqus.PublicKey == "" ||
|
|
||||||
setting.Conf.Disqus.ShortName == "" {
|
|
||||||
return ErrDisqusConfig
|
return ErrDisqusConfig
|
||||||
}
|
}
|
||||||
|
|
||||||
url := setting.Conf.Disqus.PostApprove +
|
vals := url.Values{}
|
||||||
"?api_key=" + setting.Conf.Disqus.PublicKey +
|
vals.Set("api_key", setting.Conf.Disqus.PublicKey)
|
||||||
"&access_token=" + setting.Conf.Disqus.AccessToken +
|
vals.Set("access_token", setting.Conf.Disqus.AccessToken)
|
||||||
"&post=" + post
|
vals.Set("post", post)
|
||||||
request, err := http.NewRequest("POST", url, nil)
|
|
||||||
|
request, err := http.NewRequest("POST", setting.Conf.Disqus.PostApprove, strings.NewReader(vals.Encode()))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -223,6 +235,7 @@ func PostApprove(post string) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
defer resp.Body.Close()
|
defer resp.Body.Close()
|
||||||
|
|
||||||
b, err := ioutil.ReadAll(resp.Body)
|
b, err := ioutil.ReadAll(resp.Body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -232,11 +245,57 @@ func PostApprove(post string) error {
|
|||||||
return errors.New(string(b))
|
return errors.New(string(b))
|
||||||
}
|
}
|
||||||
|
|
||||||
ar := &ApprovedResponse{}
|
result := &approvedResp{}
|
||||||
err = json.Unmarshal(b, ar)
|
err = json.Unmarshal(b, result)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 创建thread
|
||||||
|
type threadCreateResp struct {
|
||||||
|
Code int
|
||||||
|
Response struct {
|
||||||
|
Id string
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func ThreadCreate(artc *Article) error {
|
||||||
|
if !correctDisqusConfig() {
|
||||||
|
return ErrDisqusConfig
|
||||||
|
}
|
||||||
|
|
||||||
|
vals := url.Values{}
|
||||||
|
vals.Set("api_key", setting.Conf.Disqus.PublicKey)
|
||||||
|
vals.Set("access_token", setting.Conf.Disqus.AccessToken)
|
||||||
|
vals.Set("forum", setting.Conf.Disqus.ShortName)
|
||||||
|
vals.Set("title", artc.Title+" | "+Ei.BTitle)
|
||||||
|
vals.Set("identifier", "post-"+artc.Slug)
|
||||||
|
urlPath := fmt.Sprintf("https://%s/post/%s.html", setting.Conf.Mode.Domain, artc.Slug)
|
||||||
|
vals.Set("url", urlPath)
|
||||||
|
|
||||||
|
resp, err := http.PostForm(setting.Conf.Disqus.ThreadCreate, vals)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
b, err := ioutil.ReadAll(resp.Body)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if resp.StatusCode != http.StatusOK {
|
||||||
|
return errors.New(string(b))
|
||||||
|
}
|
||||||
|
|
||||||
|
result := &threadCreateResp{}
|
||||||
|
err = json.Unmarshal(b, result)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
artc.Thread = result.Response.Id
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|||||||
@@ -8,18 +8,29 @@ func TestDisqus(t *testing.T) {
|
|||||||
PostsCount()
|
PostsCount()
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestPostComment(t *testing.T) {
|
func TestPostCreate(t *testing.T) {
|
||||||
pc := &PostCreate{
|
pc := &PostComment{
|
||||||
Message: "hahahaha",
|
Message: "hahahaha",
|
||||||
Thread: "52799014",
|
Thread: "52799014",
|
||||||
AuthorEmail: "deepzz.qi@gmail.com",
|
AuthorEmail: "deepzz.qi@gmail.com",
|
||||||
AuthorName: "deepzz",
|
AuthorName: "deepzz",
|
||||||
}
|
}
|
||||||
|
|
||||||
id, err := PostComment(pc)
|
id, err := PostCreate(pc)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Error(err)
|
t.Error(err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
t.Log("post success", id)
|
t.Log("post success", id)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestThreadCreate(t *testing.T) {
|
||||||
|
tc := &Article{
|
||||||
|
Title: "测试test7",
|
||||||
|
Slug: "test7",
|
||||||
|
}
|
||||||
|
err := ThreadCreate(tc)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -6,8 +6,6 @@ services:
|
|||||||
volumes:
|
volumes:
|
||||||
- /data/eiblog/mgodb:/data/db
|
- /data/eiblog/mgodb:/data/db
|
||||||
restart: always
|
restart: always
|
||||||
ports:
|
|
||||||
- 27017:27017
|
|
||||||
elasticsearch:
|
elasticsearch:
|
||||||
image: elasticsearch:2.4.1
|
image: elasticsearch:2.4.1
|
||||||
container_name: eisearch
|
container_name: eisearch
|
||||||
@@ -35,3 +33,14 @@ services:
|
|||||||
ports:
|
ports:
|
||||||
- "9000:9000"
|
- "9000:9000"
|
||||||
restart: always
|
restart: always
|
||||||
|
# backup:
|
||||||
|
# image: registry.cn-hangzhou.aliyuncs.com/deepzz/backup
|
||||||
|
# container_name: backup
|
||||||
|
# links:
|
||||||
|
# - mongodb
|
||||||
|
# environment:
|
||||||
|
# - QINIU_BUCKET=xxxx
|
||||||
|
# - QINIU_DOMAIN=xx.example.com
|
||||||
|
# - ACCESS_KEY=xxxxxxxxxx
|
||||||
|
# - SECRECT_KEY=xxxxxxxxxx
|
||||||
|
# restart: always
|
||||||
|
|||||||
@@ -18,3 +18,7 @@ twitter:
|
|||||||

|

|
||||||
|
|
||||||
可以看到`,`之前是没有内容的,该内容是我们文章的描述。
|
可以看到`,`之前是没有内容的,该内容是我们文章的描述。
|
||||||
|
|
||||||
|
### Google OpenSearch
|
||||||
|
在 Chrome 浏览器上,你可以在输入网站后按 TAB 键进入搜索模式,如:
|
||||||
|

|
||||||
|
|||||||
@@ -6,7 +6,7 @@ $ curl -L https://github.com/eiblog/eiblog/releases/download/v1.0.0/eiblog-v1.0.
|
|||||||
|
|
||||||
2、如果有幸你也是 `Gopher`,相信你会亲自动手,你可以通过:
|
2、如果有幸你也是 `Gopher`,相信你会亲自动手,你可以通过:
|
||||||
``` sh
|
``` sh
|
||||||
$ go get -u https://github.com/eiblog/eiblog
|
$ git clone https://github.com/eiblog/eiblog.git
|
||||||
```
|
```
|
||||||
进行源码编译二进制文件运行。
|
进行源码编译二进制文件运行。
|
||||||
|
|
||||||
@@ -90,12 +90,14 @@ $ docker run -d --name eisearch \
|
|||||||
| ------------------ | ---------------------------------------- | ---------------------------------------- |
|
| ------------------ | ---------------------------------------- | ---------------------------------------- |
|
||||||
| favicon.ico | st.example.com/static/img/favicon.ico | cdn 中的文件名为 `static/img/favicon.ico`。你也可以复制 favicon.ico 到 static 文件夹下,通过 example.com/favicon.ico 也是能够访问到。docker 用户可能需要重新打包镜像。 |
|
| favicon.ico | st.example.com/static/img/favicon.ico | cdn 中的文件名为 `static/img/favicon.ico`。你也可以复制 favicon.ico 到 static 文件夹下,通过 example.com/favicon.ico 也是能够访问到。docker 用户可能需要重新打包镜像。 |
|
||||||
| bg04.jpg | st.example.com/static/img/bg04.jpg | 首页左侧的大背景图,需要更名请到 views/st_blog.css 修改。 |
|
| bg04.jpg | st.example.com/static/img/bg04.jpg | 首页左侧的大背景图,需要更名请到 views/st_blog.css 修改。 |
|
||||||
| avatar.jpg | st.example.com/static/img/avatar.jpg | 头像 |
|
| avatar.png | st.example.com/static/img/avatar.png | 头像 |
|
||||||
| blank.gif | st.example.com/static/img/blank.gif | 空白图片,[下载](https://st.deepzz.com/static/img/blank.gif) |
|
| blank.gif | st.example.com/static/img/blank.gif | 空白图片,[下载](https://st.deepzz.com/static/img/blank.gif) |
|
||||||
| default_avatar.png | st.example.com/static/img/default_avatar.png | disqus 默认图片,[下载](https://st.deepzz.com/static/img/default_avatar.png) |
|
| default_avatar.png | st.example.com/static/img/default_avatar.png | disqus 默认图片,[下载](https://st.deepzz.com/static/img/default_avatar.png) |
|
||||||
| disqus.js | st.example.com/static/js/disqus_xxx.js | disqus 文件,你可以通过 https://short_name.disqus.com/embed.js 下载你的专属文件,并上传到七牛。更新配置文件 app.yml。 |
|
| disqus.js | st.example.com/static/js/disqus_xxx.js | disqus 文件,你可以通过 https://short_name.disqus.com/embed.js 下载你的专属文件,并上传到七牛。更新配置文件 app.yml。 |
|
||||||
|
|
||||||
> 注意:每次修改 views 内的以 `st_` 开头的文件,请将 `app.yml` 中的 staticversion 提高一个版本。 cdn 提到的文件下载,请复制链接进行下载,因为博主使用了防盗链功能。
|
> 注意,cdn 提到的文件下载,请复制链接进行下载,因为博主使用了防盗链功能,还有:
|
||||||
|
1、每次修改 app.yml 文件(如:更换 cdn 域名或更新头像),如果你不知道是否应该提高 staticversion 一个版本,那么最好提高一个 +1。
|
||||||
|
2、每次手动修改 views 内的以 `st_` 开头的文件,请将 `app.yml` 中的 staticversion 提高一个版本。
|
||||||
|
|
||||||
#### 配置说明
|
#### 配置说明
|
||||||
走到这里,我相信只走到 `60%` 的路程。放弃还来得及。
|
走到这里,我相信只走到 `60%` 的路程。放弃还来得及。
|
||||||
@@ -141,7 +143,7 @@ $ docker run -d --name eisearch \
|
|||||||
| ----------- | ---------------------------------------- |
|
| ----------- | ---------------------------------------- |
|
||||||
| app.yml | 整个程序的配置文件,里面已经列出了所有配置项的说明,这里不再阐述。 |
|
| app.yml | 整个程序的配置文件,里面已经列出了所有配置项的说明,这里不再阐述。 |
|
||||||
| blackip.yml | 如果没有使用 `Nginx`,博客内置 `ip` 过滤系统。 |
|
| blackip.yml | 如果没有使用 `Nginx`,博客内置 `ip` 过滤系统。 |
|
||||||
| es | elasticsearch,非常强大的分布式搜索引擎,`github` 用的就是它。里面的配置基本不用修改,但 `es/analysis/synonym.txt` 是同义词,你可以照着已有的随意增加。注意,scripts 虽然是空的,但请保持存在。 |
|
| es | elasticsearch,非常强大的分布式搜索引擎,`github` 用的就是它。里面的配置基本不用修改,但 `es/analysis/synonym.txt` 是同义词,你可以照着已有的随意增加。scripts 是 es 的脚本文件夹 |
|
||||||
| nginx | 系统采用 `nginx` 作为代理(相信博客系统也不会独占一台服务器~)。请使用 `nginx.conf` 替换原 `nginx` 的配置。博客系统的配置文件是 `domain/eiblog.conf`,或则重命名(只要是满足`*.conf`)。`eiblog.conf`文件里面学问是最多的。或许你想一一弄懂,或许…。注意本配置需要更新 nginx 到最新版,openssl 更新到1.0.2j,具体请到 Jerry Qu 的 [本博客 Nginx 配置之完整篇](https://imququ.com/post/my-nginx-conf.html) 查看,了解详情。 |
|
| nginx | 系统采用 `nginx` 作为代理(相信博客系统也不会独占一台服务器~)。请使用 `nginx.conf` 替换原 `nginx` 的配置。博客系统的配置文件是 `domain/eiblog.conf`,或则重命名(只要是满足`*.conf`)。`eiblog.conf`文件里面学问是最多的。或许你想一一弄懂,或许…。注意本配置需要更新 nginx 到最新版,openssl 更新到1.0.2j,具体请到 Jerry Qu 的 [本博客 Nginx 配置之完整篇](https://imququ.com/post/my-nginx-conf.html) 查看,了解详情。 |
|
||||||
| scts | 存放 ct 文件。 |
|
| scts | 存放 ct 文件。 |
|
||||||
| ssl | 这里存放了所有证书相关的内容。 |
|
| ssl | 这里存放了所有证书相关的内容。 |
|
||||||
@@ -186,8 +188,6 @@ $ docker run -d --name eiblog --restart=always \
|
|||||||
|
|
||||||
首先,请将本地测试好的 `conf`,`docker-compose.yml` 文件夹和文件上传至服务器。`conf` 建议存储到服务器 `/data/eiblog` 下,`docker-compose.yml` 存放在你使用方便的地方。
|
首先,请将本地测试好的 `conf`,`docker-compose.yml` 文件夹和文件上传至服务器。`conf` 建议存储到服务器 `/data/eiblog` 下,`docker-compose.yml` 存放在你使用方便的地方。
|
||||||
|
|
||||||
> 注意检查 `conf/es/config/scripts` 空文件夹是否存在,不存在即创建。
|
|
||||||
|
|
||||||
``` sh
|
``` sh
|
||||||
$ tree /data/eiblog -L 1
|
$ tree /data/eiblog -L 1
|
||||||
|
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
"regexp"
|
"regexp"
|
||||||
"strings"
|
"strings"
|
||||||
@@ -23,13 +24,25 @@ const (
|
|||||||
ES_DATE = `{"range":{"date":{"gte":"%s","lte": "%s","format": "yyyy-MM-dd||yyyy-MM||yyyy"}}}` // 2016-10||/M
|
ES_DATE = `{"range":{"date":{"gte":"%s","lte": "%s","format": "yyyy-MM-dd||yyyy-MM||yyyy"}}}` // 2016-10||/M
|
||||||
)
|
)
|
||||||
|
|
||||||
var es *ElasticService
|
var (
|
||||||
|
ErrUninitializedES = errors.New("uninitialized elasticsearch")
|
||||||
|
|
||||||
|
es *ElasticService
|
||||||
|
)
|
||||||
|
|
||||||
|
// 初始化 Elasticsearch 服务器
|
||||||
func init() {
|
func init() {
|
||||||
|
_, err := net.LookupIP("elasticsearch")
|
||||||
|
if err != nil {
|
||||||
|
logd.Info(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
es = &ElasticService{url: "http://elasticsearch:9200", c: new(http.Client)}
|
es = &ElasticService{url: "http://elasticsearch:9200", c: new(http.Client)}
|
||||||
initIndex()
|
initIndex()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 创建索引
|
||||||
func initIndex() {
|
func initIndex() {
|
||||||
mappings := fmt.Sprintf(`{"mappings":{"%s":{"properties":{"content":{"analyzer":"ik_syno","search_analyzer":"ik_syno","term_vector":"with_positions_offsets","type":"string"},"date":{"index":"not_analyzed","type":"date"},"slug":{"type":"string"},"tag":{"index":"not_analyzed","type":"string"},"title":{"analyzer":"ik_syno","search_analyzer":"ik_syno","term_vector":"with_positions_offsets","type":"string"}}}}}`, TYPE)
|
mappings := fmt.Sprintf(`{"mappings":{"%s":{"properties":{"content":{"analyzer":"ik_syno","search_analyzer":"ik_syno","term_vector":"with_positions_offsets","type":"string"},"date":{"index":"not_analyzed","type":"date"},"slug":{"type":"string"},"tag":{"index":"not_analyzed","type":"string"},"title":{"analyzer":"ik_syno","search_analyzer":"ik_syno","term_vector":"with_positions_offsets","type":"string"}}}}}`, TYPE)
|
||||||
err := CreateIndexAndMappings(INDEX, TYPE, []byte(mappings))
|
err := CreateIndexAndMappings(INDEX, TYPE, []byte(mappings))
|
||||||
@@ -38,7 +51,12 @@ func initIndex() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func Elasticsearch(qStr string, size, from int) *ESSearchResult {
|
// 查询
|
||||||
|
func Elasticsearch(qStr string, size, from int) (*ESSearchResult, error) {
|
||||||
|
if es == nil {
|
||||||
|
return nil, ErrUninitializedES
|
||||||
|
}
|
||||||
|
|
||||||
// 分析查询字符串
|
// 分析查询字符串
|
||||||
reg := regexp.MustCompile(`(tag|slug|date):`)
|
reg := regexp.MustCompile(`(tag|slug|date):`)
|
||||||
indexs := reg.FindAllStringIndex(qStr, -1)
|
indexs := reg.FindAllStringIndex(qStr, -1)
|
||||||
@@ -89,13 +107,17 @@ func Elasticsearch(qStr string, size, from int) *ESSearchResult {
|
|||||||
}
|
}
|
||||||
docs, err := IndexQueryDSL(INDEX, TYPE, size, from, []byte(dsl))
|
docs, err := IndexQueryDSL(INDEX, TYPE, size, from, []byte(dsl))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logd.Error(err)
|
return nil, err
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
return docs
|
return docs, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 添加或更新索引
|
||||||
func ElasticIndex(artc *Article) error {
|
func ElasticIndex(artc *Article) error {
|
||||||
|
if es == nil {
|
||||||
|
return ErrUninitializedES
|
||||||
|
}
|
||||||
|
|
||||||
img := PickFirstImage(artc.Content)
|
img := PickFirstImage(artc.Content)
|
||||||
mapping := map[string]interface{}{
|
mapping := map[string]interface{}{
|
||||||
"title": artc.Title,
|
"title": artc.Title,
|
||||||
@@ -109,7 +131,12 @@ func ElasticIndex(artc *Article) error {
|
|||||||
return IndexOrUpdateDocument(INDEX, TYPE, artc.ID, b)
|
return IndexOrUpdateDocument(INDEX, TYPE, artc.ID, b)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 删除索引
|
||||||
func ElasticDelIndex(ids []int32) error {
|
func ElasticDelIndex(ids []int32) error {
|
||||||
|
if es == nil {
|
||||||
|
return ErrUninitializedES
|
||||||
|
}
|
||||||
|
|
||||||
var target []string
|
var target []string
|
||||||
for _, id := range ids {
|
for _, id := range ids {
|
||||||
target = append(target, fmt.Sprint(id))
|
target = append(target, fmt.Sprint(id))
|
||||||
@@ -127,10 +154,12 @@ type IndicesCreateResult struct {
|
|||||||
Acknowledged bool `json:"acknowledged"`
|
Acknowledged bool `json:"acknowledged"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 返回 url
|
||||||
func (s *ElasticService) ParseURL(format string, params ...interface{}) string {
|
func (s *ElasticService) ParseURL(format string, params ...interface{}) string {
|
||||||
return fmt.Sprintf(s.url+format, params...)
|
return fmt.Sprintf(s.url+format, params...)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Elastic 相关操作请求
|
||||||
func (s *ElasticService) Do(req *http.Request) (interface{}, error) {
|
func (s *ElasticService) Do(req *http.Request) (interface{}, error) {
|
||||||
resp, err := s.c.Do(req)
|
resp, err := s.c.Do(req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -152,11 +181,8 @@ func (s *ElasticService) Do(req *http.Request) (interface{}, error) {
|
|||||||
return b, nil
|
return b, nil
|
||||||
case "HEAD":
|
case "HEAD":
|
||||||
return resp.StatusCode, nil
|
return resp.StatusCode, nil
|
||||||
|
|
||||||
default:
|
|
||||||
return nil, errors.New("unknown methods")
|
|
||||||
}
|
}
|
||||||
return nil, nil
|
return nil, errors.New("unknown methods")
|
||||||
}
|
}
|
||||||
|
|
||||||
func CreateIndexAndMappings(index, typ string, mappings []byte) (err error) {
|
func CreateIndexAndMappings(index, typ string, mappings []byte) (err error) {
|
||||||
@@ -187,6 +213,7 @@ func CreateIndexAndMappings(index, typ string, mappings []byte) (err error) {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 创建或更新索引
|
||||||
func IndexOrUpdateDocument(index, typ string, id int32, doc []byte) (err error) {
|
func IndexOrUpdateDocument(index, typ string, id int32, doc []byte) (err error) {
|
||||||
req, err := http.NewRequest("PUT", es.ParseURL("/%s/%s/%d", index, typ, id), bytes.NewReader(doc))
|
req, err := http.NewRequest("PUT", es.ParseURL("/%s/%s/%d", index, typ, id), bytes.NewReader(doc))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -213,6 +240,7 @@ type ESDeleteResult struct {
|
|||||||
} `json:"iterms"`
|
} `json:"iterms"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 删除文档
|
||||||
func DeleteDocument(index, typ string, ids []string) error {
|
func DeleteDocument(index, typ string, ids []string) error {
|
||||||
var buff bytes.Buffer
|
var buff bytes.Buffer
|
||||||
for _, id := range ids {
|
for _, id := range ids {
|
||||||
@@ -247,6 +275,7 @@ func DeleteDocument(index, typ string, ids []string) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 查询结果
|
||||||
type ESSearchResult struct {
|
type ESSearchResult struct {
|
||||||
Took float32 `json:"took"`
|
Took float32 `json:"took"`
|
||||||
Hits struct {
|
Hits struct {
|
||||||
@@ -268,6 +297,7 @@ type ESSearchResult struct {
|
|||||||
} `json:"hits"`
|
} `json:"hits"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DSL 语句查询文档
|
||||||
func IndexQueryDSL(index, typ string, size, from int, dsl []byte) (*ESSearchResult, error) {
|
func IndexQueryDSL(index, typ string, size, from int, dsl []byte) (*ESSearchResult, error) {
|
||||||
req, err := http.NewRequest("POST", es.ParseURL("/%s/%s/_search?size=%d&from=%d", index, typ, size, from), bytes.NewReader(dsl))
|
req, err := http.NewRequest("POST", es.ParseURL("/%s/%s/_search?size=%d&from=%d", index, typ, size, from), bytes.NewReader(dsl))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
74
front.go
74
front.go
@@ -86,11 +86,12 @@ func GetBase() gin.H {
|
|||||||
"BTitle": Ei.BTitle,
|
"BTitle": Ei.BTitle,
|
||||||
"BeiAn": Ei.BeiAn,
|
"BeiAn": Ei.BeiAn,
|
||||||
"Domain": setting.Conf.Mode.Domain,
|
"Domain": setting.Conf.Mode.Domain,
|
||||||
"Kodo": setting.Conf.Kodo,
|
"Qiniu": setting.Conf.Qiniu,
|
||||||
"Disqus": setting.Conf.Disqus,
|
"Disqus": setting.Conf.Disqus,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// not found
|
||||||
func HandleNotFound(c *gin.Context) {
|
func HandleNotFound(c *gin.Context) {
|
||||||
h := GetBase()
|
h := GetBase()
|
||||||
h["Version"] = StaticVersion(c)
|
h["Version"] = StaticVersion(c)
|
||||||
@@ -101,6 +102,7 @@ func HandleNotFound(c *gin.Context) {
|
|||||||
RenderHTMLFront(c, "notfound", h)
|
RenderHTMLFront(c, "notfound", h)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 首页
|
||||||
func HandleHomePage(c *gin.Context) {
|
func HandleHomePage(c *gin.Context) {
|
||||||
h := GetBase()
|
h := GetBase()
|
||||||
h["Version"] = StaticVersion(c)
|
h["Version"] = StaticVersion(c)
|
||||||
@@ -117,6 +119,7 @@ func HandleHomePage(c *gin.Context) {
|
|||||||
RenderHTMLFront(c, "home", h)
|
RenderHTMLFront(c, "home", h)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 专题页
|
||||||
func HandleSeriesPage(c *gin.Context) {
|
func HandleSeriesPage(c *gin.Context) {
|
||||||
h := GetBase()
|
h := GetBase()
|
||||||
h["Version"] = StaticVersion(c)
|
h["Version"] = StaticVersion(c)
|
||||||
@@ -129,6 +132,7 @@ func HandleSeriesPage(c *gin.Context) {
|
|||||||
RenderHTMLFront(c, "series", h)
|
RenderHTMLFront(c, "series", h)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 归档页
|
||||||
func HandleArchivesPage(c *gin.Context) {
|
func HandleArchivesPage(c *gin.Context) {
|
||||||
h := GetBase()
|
h := GetBase()
|
||||||
h["Version"] = StaticVersion(c)
|
h["Version"] = StaticVersion(c)
|
||||||
@@ -141,6 +145,7 @@ func HandleArchivesPage(c *gin.Context) {
|
|||||||
RenderHTMLFront(c, "archives", h)
|
RenderHTMLFront(c, "archives", h)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 文章
|
||||||
func HandleArticlePage(c *gin.Context) {
|
func HandleArticlePage(c *gin.Context) {
|
||||||
path := c.Param("slug")
|
path := c.Param("slug")
|
||||||
if !strings.HasSuffix(path, ".html") || Ei.MapArticles[path[:len(path)-5]] == nil {
|
if !strings.HasSuffix(path, ".html") || Ei.MapArticles[path[:len(path)-5]] == nil {
|
||||||
@@ -178,6 +183,7 @@ func HandleArticlePage(c *gin.Context) {
|
|||||||
RenderHTMLFront(c, name, h)
|
RenderHTMLFront(c, name, h)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 搜索页
|
||||||
func HandleSearchPage(c *gin.Context) {
|
func HandleSearchPage(c *gin.Context) {
|
||||||
h := GetBase()
|
h := GetBase()
|
||||||
h["Version"] = StaticVersion(c)
|
h["Version"] = StaticVersion(c)
|
||||||
@@ -193,10 +199,12 @@ func HandleSearchPage(c *gin.Context) {
|
|||||||
start = 1
|
start = 1
|
||||||
}
|
}
|
||||||
h["Word"] = q
|
h["Word"] = q
|
||||||
var result *ESSearchResult
|
|
||||||
vals := c.Request.URL.Query()
|
vals := c.Request.URL.Query()
|
||||||
result = Elasticsearch(q, setting.Conf.General.PageNum, start-1)
|
result, err := Elasticsearch(q, setting.Conf.General.PageNum, start-1)
|
||||||
if result != nil {
|
if err != nil {
|
||||||
|
logd.Error(err)
|
||||||
|
} else {
|
||||||
result.Took /= 1000
|
result.Took /= 1000
|
||||||
for i, v := range result.Hits.Hits {
|
for i, v := range result.Hits.Hits {
|
||||||
if artc := Ei.MapArticles[result.Hits.Hits[i].Source.Slug]; len(v.Highlight.Content) == 0 && artc != nil {
|
if artc := Ei.MapArticles[result.Hits.Hits[i].Source.Slug]; len(v.Highlight.Content) == 0 && artc != nil {
|
||||||
@@ -220,6 +228,7 @@ func HandleSearchPage(c *gin.Context) {
|
|||||||
RenderHTMLFront(c, "search", h)
|
RenderHTMLFront(c, "search", h)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 评论页
|
||||||
func HandleDisqusFrom(c *gin.Context) {
|
func HandleDisqusFrom(c *gin.Context) {
|
||||||
params := strings.Split(c.Param("slug"), "|")
|
params := strings.Split(c.Param("slug"), "|")
|
||||||
if len(params) != 4 || params[1] == "" {
|
if len(params) != 4 || params[1] == "" {
|
||||||
@@ -240,26 +249,32 @@ func HandleDisqusFrom(c *gin.Context) {
|
|||||||
c.Header("Content-Type", "text/html; charset=utf-8")
|
c.Header("Content-Type", "text/html; charset=utf-8")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// feed
|
||||||
func HandleFeed(c *gin.Context) {
|
func HandleFeed(c *gin.Context) {
|
||||||
http.ServeFile(c.Writer, c.Request, "static/feed.xml")
|
http.ServeFile(c.Writer, c.Request, "static/feed.xml")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// opensearch
|
||||||
func HandleOpenSearch(c *gin.Context) {
|
func HandleOpenSearch(c *gin.Context) {
|
||||||
http.ServeFile(c.Writer, c.Request, "static/opensearch.xml")
|
http.ServeFile(c.Writer, c.Request, "static/opensearch.xml")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// robots
|
||||||
func HandleRobots(c *gin.Context) {
|
func HandleRobots(c *gin.Context) {
|
||||||
http.ServeFile(c.Writer, c.Request, "static/robots.txt")
|
http.ServeFile(c.Writer, c.Request, "static/robots.txt")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// sitemap
|
||||||
func HandleSitemap(c *gin.Context) {
|
func HandleSitemap(c *gin.Context) {
|
||||||
http.ServeFile(c.Writer, c.Request, "static/sitemap.xml")
|
http.ServeFile(c.Writer, c.Request, "static/sitemap.xml")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// cross domain
|
||||||
func HandleCrossDomain(c *gin.Context) {
|
func HandleCrossDomain(c *gin.Context) {
|
||||||
http.ServeFile(c.Writer, c.Request, "static/crossdomain.xml")
|
http.ServeFile(c.Writer, c.Request, "static/crossdomain.xml")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// favicon
|
||||||
func HandleFavicon(c *gin.Context) {
|
func HandleFavicon(c *gin.Context) {
|
||||||
http.ServeFile(c.Writer, c.Request, "static/favicon.ico")
|
http.ServeFile(c.Writer, c.Request, "static/favicon.ico")
|
||||||
}
|
}
|
||||||
@@ -321,9 +336,9 @@ type commentsDetail struct {
|
|||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
Url string `json:"url"`
|
Url string `json:"url"`
|
||||||
Avatar string `json:"avatar"`
|
Avatar string `json:"avatar"`
|
||||||
CreatedAt string `json:"createdAt"`
|
|
||||||
CreatedAtStr string `json:"createdAtStr"`
|
CreatedAtStr string `json:"createdAtStr"`
|
||||||
Message string `json:"message"`
|
Message string `json:"message"`
|
||||||
|
IsDeleted bool `json:"isDeleted"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func HandleDisqus(c *gin.Context) {
|
func HandleDisqus(c *gin.Context) {
|
||||||
@@ -356,30 +371,39 @@ func HandleDisqus(c *gin.Context) {
|
|||||||
Parent: v.Parent,
|
Parent: v.Parent,
|
||||||
Url: v.Author.ProfileUrl,
|
Url: v.Author.ProfileUrl,
|
||||||
Avatar: v.Author.Avatar.Cache,
|
Avatar: v.Author.Avatar.Cache,
|
||||||
CreatedAt: v.CreatedAt,
|
|
||||||
CreatedAtStr: ConvertStr(v.CreatedAt),
|
CreatedAtStr: ConvertStr(v.CreatedAt),
|
||||||
Message: v.Message,
|
Message: v.Message,
|
||||||
|
IsDeleted: v.IsDeleted,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
c.JSON(http.StatusOK, dcs)
|
c.JSON(http.StatusOK, dcs)
|
||||||
}
|
}
|
||||||
|
|
||||||
// [thread:[5279901489] parent:[] identifier:[post-troubleshooting-https] next:[] author_name:[你好] author_email:[chenqijing2@163.com] message:[fdsfdsf]]
|
// 发表评论
|
||||||
|
// [thread:[5279901489] parent:[] identifier:[post-troubleshooting-https]
|
||||||
|
// next:[] author_name:[你好] author_email:[chenqijing2@163.com] message:[fdsfdsf]]
|
||||||
|
type DisqusCreate struct {
|
||||||
|
ErrNo int `json:"errno"`
|
||||||
|
ErrMsg string `json:"errmsg"`
|
||||||
|
Data commentsDetail `json:"data"`
|
||||||
|
}
|
||||||
|
|
||||||
func HandleDisqusCreate(c *gin.Context) {
|
func HandleDisqusCreate(c *gin.Context) {
|
||||||
rep := gin.H{"errno": SUCCESS, "errmsg": ""}
|
resp := &DisqusCreate{}
|
||||||
defer c.JSON(http.StatusOK, rep)
|
defer c.JSON(http.StatusOK, resp)
|
||||||
|
|
||||||
msg := c.PostForm("message")
|
msg := c.PostForm("message")
|
||||||
email := c.PostForm("author_email")
|
email := c.PostForm("author_email")
|
||||||
name := c.PostForm("author_name")
|
name := c.PostForm("author_name")
|
||||||
thread := c.PostForm("thread")
|
thread := c.PostForm("thread")
|
||||||
identifier := c.PostForm("identifier")
|
identifier := c.PostForm("identifier")
|
||||||
if msg == "" || email == "" || name == "" || thread == "" || identifier == "" {
|
if msg == "" || email == "" || name == "" || thread == "" || identifier == "" {
|
||||||
rep["errno"] = FAIL
|
resp.ErrNo = FAIL
|
||||||
rep["errmsg"] = "参数错误"
|
resp.ErrMsg = "参数错误"
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
pc := &PostCreate{
|
pc := &PostComment{
|
||||||
Message: msg,
|
Message: msg,
|
||||||
Parent: c.PostForm("parent"),
|
Parent: c.PostForm("parent"),
|
||||||
Thread: thread,
|
Thread: thread,
|
||||||
@@ -389,24 +413,34 @@ func HandleDisqusCreate(c *gin.Context) {
|
|||||||
IpAddress: c.ClientIP(),
|
IpAddress: c.ClientIP(),
|
||||||
}
|
}
|
||||||
|
|
||||||
id, err := PostComment(pc)
|
postDetail, err := PostCreate(pc)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logd.Error(err)
|
logd.Error(err)
|
||||||
rep["errno"] = FAIL
|
resp.ErrNo = FAIL
|
||||||
rep["errmsg"] = "系统错误"
|
resp.ErrMsg = "系统错误"
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
err = PostApprove(id)
|
err = PostApprove(postDetail.Response.Id)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logd.Error(err)
|
logd.Error(err)
|
||||||
rep["errno"] = FAIL
|
resp.ErrNo = FAIL
|
||||||
rep["errmsg"] = "系统错误"
|
resp.ErrMsg = "系统错误"
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
rep["errno"] = SUCCESS
|
resp.ErrNo = SUCCESS
|
||||||
rep["data"] = gin.H{"id": id}
|
resp.Data = commentsDetail{
|
||||||
|
Id: postDetail.Response.Id,
|
||||||
|
Name: name,
|
||||||
|
Parent: postDetail.Response.Parent,
|
||||||
|
Url: postDetail.Response.Author.ProfileUrl,
|
||||||
|
Avatar: postDetail.Response.Author.Avatar.Cache,
|
||||||
|
CreatedAtStr: ConvertStr(postDetail.Response.CreatedAt),
|
||||||
|
Message: postDetail.Response.Message,
|
||||||
|
IsDeleted: postDetail.Response.IsDeleted,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 渲染页面
|
||||||
func RenderHTMLFront(c *gin.Context, name string, data gin.H) {
|
func RenderHTMLFront(c *gin.Context, name string, data gin.H) {
|
||||||
var buf bytes.Buffer
|
var buf bytes.Buffer
|
||||||
err := Tmpl.ExecuteTemplate(&buf, name, data)
|
err := Tmpl.ExecuteTemplate(&buf, name, data)
|
||||||
|
|||||||
43
glide.lock
generated
43
glide.lock
generated
@@ -1,28 +1,28 @@
|
|||||||
hash: bd360fa297ed66950543990f9433cdcdf13c29dd99d9a01b49027e236b2cb9da
|
hash: c733fa4abeda21b59b001578b37a168bd33038d337b61198cc5fd94be8bfdf77
|
||||||
updated: 2017-07-13T01:29:28.226895963+08:00
|
updated: 2018-01-13T18:22:28.620808+08:00
|
||||||
imports:
|
imports:
|
||||||
- name: github.com/boj/redistore
|
- name: github.com/boj/redistore
|
||||||
version: 4562487a4bee9a7c272b72bfaeda4917d0a47ab9
|
version: 4562487a4bee9a7c272b72bfaeda4917d0a47ab9
|
||||||
- name: github.com/deepzz0/logd
|
- name: github.com/deepzz0/logd
|
||||||
version: 2bbe53d047054777f3a171cdfc6dca7aa9f8af78
|
version: f91dd8c6316f0e156e93895a96739b67577b6a63
|
||||||
- name: github.com/eiblog/blackfriday
|
- name: github.com/eiblog/blackfriday
|
||||||
version: c0ec111761ae784fe31cc076f2fa0e2d2216d623
|
version: c0ec111761ae784fe31cc076f2fa0e2d2216d623
|
||||||
- name: github.com/eiblog/utils
|
- name: github.com/eiblog/utils
|
||||||
version: ddfd888542f9a093000f71c3709009c1440a0789
|
version: e8f16268dae939f920ddc55f1c9e46a97a5e3559
|
||||||
subpackages:
|
subpackages:
|
||||||
- logd
|
- logd
|
||||||
- mgo
|
- mgo
|
||||||
- tmpl
|
- tmpl
|
||||||
- uuid
|
- uuid
|
||||||
- name: github.com/garyburd/redigo
|
- name: github.com/garyburd/redigo
|
||||||
version: 9f3a0116c9f72c5a56f958206a43dc881b502c37
|
version: d1ed5c67e5794de818ea85e6b522fda02623a484
|
||||||
subpackages:
|
subpackages:
|
||||||
- internal
|
- internal
|
||||||
- redis
|
- redis
|
||||||
- name: github.com/gin-gonic/autotls
|
- name: github.com/gin-gonic/autotls
|
||||||
version: 9261e1c52a0eb595c531ff77c06cdfb6fdb111a4
|
version: 8ca25fbde72bb72a00466215b94b489c71fcb815
|
||||||
- name: github.com/gin-gonic/contrib
|
- name: github.com/gin-gonic/contrib
|
||||||
version: d4fc5a96cc0d29cb0e862bb1312dd6f4fedfcaee
|
version: 88aede40372d4bcb11e45168a8c30d99e44cf617
|
||||||
subpackages:
|
subpackages:
|
||||||
- sessions
|
- sessions
|
||||||
- name: github.com/gin-gonic/gin
|
- name: github.com/gin-gonic/gin
|
||||||
@@ -39,32 +39,29 @@ imports:
|
|||||||
- name: github.com/gorilla/securecookie
|
- name: github.com/gorilla/securecookie
|
||||||
version: e59506cc896acb7f7bf732d4fdf5e25f7ccd8983
|
version: e59506cc896acb7f7bf732d4fdf5e25f7ccd8983
|
||||||
- name: github.com/gorilla/sessions
|
- name: github.com/gorilla/sessions
|
||||||
version: 8b6b4cd75f07f7ee036eb37b8127bd40ab1efc49
|
version: a3acf13e802c358d65f249324d14ed24aac11370
|
||||||
- name: github.com/manucorporat/sse
|
- name: github.com/manucorporat/sse
|
||||||
version: ee05b128a739a0fb76c7ebd3ae4810c1de808d6d
|
version: ee05b128a739a0fb76c7ebd3ae4810c1de808d6d
|
||||||
- name: github.com/mattn/go-isatty
|
- name: github.com/mattn/go-isatty
|
||||||
version: fc9e8d8ef48496124e79ae0df75490096eccf6fe
|
version: 6ca4dbf54d38eea1a992b3c722a76a5d1c4cb25c
|
||||||
- name: github.com/qiniu/api.v7
|
- name: github.com/qiniu/api.v7
|
||||||
version: 9c12a67868f8f94d6a75dd6bb59b095db8d40d77
|
version: b7c7d6a2ce0aff8e5e7d14c39c3cde867efa1123
|
||||||
subpackages:
|
subpackages:
|
||||||
- api
|
|
||||||
- auth/qbox
|
- auth/qbox
|
||||||
- conf
|
- conf
|
||||||
- kodocli
|
- storage
|
||||||
- name: github.com/qiniu/x
|
- name: github.com/qiniu/x
|
||||||
version: f512abcf45ab4e2ba0fd4784c57b53d495997d66
|
version: f512abcf45ab4e2ba0fd4784c57b53d495997d66
|
||||||
subpackages:
|
subpackages:
|
||||||
- bytes.v7
|
- bytes.v7
|
||||||
- bytes.v7/seekable
|
- bytes.v7/seekable
|
||||||
- ctype.v7
|
- ctype.v7
|
||||||
- log.v7
|
|
||||||
- rpc.v7
|
- rpc.v7
|
||||||
- url.v7
|
|
||||||
- xlog.v7
|
- xlog.v7
|
||||||
- name: github.com/shurcooL/sanitized_anchor_name
|
- name: github.com/shurcooL/sanitized_anchor_name
|
||||||
version: 541ff5ee47f1dddf6a5281af78307d921524bcb5
|
version: 86672fcb3f950f35f2e675df2240550f2a50762f
|
||||||
- name: golang.org/x/crypto
|
- name: golang.org/x/crypto
|
||||||
version: dd85ac7e6a88fc6ca420478e934de5f1a42dd3c6
|
version: 13931e22f9e72ea58bb73048bc752b48c6d4d4ac
|
||||||
subpackages:
|
subpackages:
|
||||||
- acme
|
- acme
|
||||||
- acme/autocert
|
- acme/autocert
|
||||||
@@ -73,7 +70,7 @@ imports:
|
|||||||
subpackages:
|
subpackages:
|
||||||
- context
|
- context
|
||||||
- name: golang.org/x/sys
|
- name: golang.org/x/sys
|
||||||
version: abf9c25f54453410d0c6668e519582a9e1115027
|
version: 810d7000345868fc619eb81f46307107118f4ae1
|
||||||
subpackages:
|
subpackages:
|
||||||
- unix
|
- unix
|
||||||
- name: gopkg.in/go-playground/validator.v8
|
- name: gopkg.in/go-playground/validator.v8
|
||||||
@@ -86,19 +83,13 @@ imports:
|
|||||||
- internal/sasl
|
- internal/sasl
|
||||||
- internal/scram
|
- internal/scram
|
||||||
- name: gopkg.in/yaml.v2
|
- name: gopkg.in/yaml.v2
|
||||||
version: 1be3d31502d6eabc0dd7ce5b0daab022e14a5538
|
version: d670f9405373e636a5a2765eea47fac0c9bc91a4
|
||||||
- name: qiniupkg.com/api.v7
|
|
||||||
version: 9c12a67868f8f94d6a75dd6bb59b095db8d40d77
|
|
||||||
subpackages:
|
|
||||||
- kodo
|
|
||||||
- kodocli
|
|
||||||
- name: qiniupkg.com/x
|
- name: qiniupkg.com/x
|
||||||
version: f512abcf45ab4e2ba0fd4784c57b53d495997d66
|
version: 946c4a16076d6d98aeb78619e2bd4012357f7228
|
||||||
subpackages:
|
subpackages:
|
||||||
- bytes.v7
|
- bytes.v7
|
||||||
- log.v7
|
- log.v7
|
||||||
- reqid.v7
|
- reqid.v7
|
||||||
- url.v7
|
|
||||||
testImports:
|
testImports:
|
||||||
- name: github.com/davecgh/go-spew
|
- name: github.com/davecgh/go-spew
|
||||||
version: 5215b55f46b2b919f50a1df0eaa5886afe4e3b3d
|
version: 5215b55f46b2b919f50a1df0eaa5886afe4e3b3d
|
||||||
@@ -109,6 +100,6 @@ testImports:
|
|||||||
subpackages:
|
subpackages:
|
||||||
- difflib
|
- difflib
|
||||||
- name: github.com/stretchr/testify
|
- name: github.com/stretchr/testify
|
||||||
version: f390dcf405f7b83c997eac1b06768bb9f44dec18
|
version: 69483b4bd14f5845b5a1e55bca19e954e827f1d0
|
||||||
subpackages:
|
subpackages:
|
||||||
- assert
|
- assert
|
||||||
|
|||||||
16
glide.yaml
16
glide.yaml
@@ -1,5 +1,6 @@
|
|||||||
package: github.com/eiblog/eiblog
|
package: github.com/eiblog/eiblog
|
||||||
import:
|
import:
|
||||||
|
- package: github.com/deepzz0/logd
|
||||||
- package: github.com/eiblog/blackfriday
|
- package: github.com/eiblog/blackfriday
|
||||||
- package: github.com/eiblog/utils
|
- package: github.com/eiblog/utils
|
||||||
subpackages:
|
subpackages:
|
||||||
@@ -7,19 +8,20 @@ import:
|
|||||||
- mgo
|
- mgo
|
||||||
- tmpl
|
- tmpl
|
||||||
- uuid
|
- uuid
|
||||||
|
- package: github.com/gin-gonic/autotls
|
||||||
- package: github.com/gin-gonic/contrib
|
- package: github.com/gin-gonic/contrib
|
||||||
subpackages:
|
subpackages:
|
||||||
- sessions
|
- sessions
|
||||||
- package: github.com/gin-gonic/gin
|
- package: github.com/gin-gonic/gin
|
||||||
version: ~1.1.4
|
- package: github.com/qiniu/api.v7
|
||||||
|
subpackages:
|
||||||
|
- auth/qbox
|
||||||
|
- storage
|
||||||
- package: gopkg.in/mgo.v2
|
- package: gopkg.in/mgo.v2
|
||||||
subpackages:
|
subpackages:
|
||||||
- bson
|
- bson
|
||||||
- package: gopkg.in/yaml.v2
|
- package: gopkg.in/yaml.v2
|
||||||
- package: qiniupkg.com/api.v7
|
testImport:
|
||||||
|
- package: github.com/stretchr/testify
|
||||||
subpackages:
|
subpackages:
|
||||||
- kodo
|
- assert
|
||||||
- kodocli
|
|
||||||
- package: qiniupkg.com/x
|
|
||||||
subpackages:
|
|
||||||
- url.v7
|
|
||||||
|
|||||||
21
helper.go
21
helper.go
@@ -18,6 +18,7 @@ const (
|
|||||||
FAIL
|
FAIL
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// 月份转换
|
||||||
var monthToDays = map[time.Month]int{
|
var monthToDays = map[time.Month]int{
|
||||||
time.January: 31,
|
time.January: 31,
|
||||||
time.February: 28,
|
time.February: 28,
|
||||||
@@ -43,14 +44,17 @@ func EncryptPasswd(name, pass string) string {
|
|||||||
return fmt.Sprintf("%x", h.Sum(nil))
|
return fmt.Sprintf("%x", h.Sum(nil))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 验证密码
|
||||||
func VerifyPasswd(origin, name, input string) bool {
|
func VerifyPasswd(origin, name, input string) bool {
|
||||||
return origin == EncryptPasswd(name, input)
|
return origin == EncryptPasswd(name, input)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 随机 uuid
|
||||||
func RandUUIDv4() string {
|
func RandUUIDv4() string {
|
||||||
return uuid.NewV4().String()
|
return uuid.NewV4().String()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 读取目录
|
||||||
func ReadDir(dir string, filter func(name string) bool) (files []string) {
|
func ReadDir(dir string, filter func(name string) bool) (files []string) {
|
||||||
fis, err := ioutil.ReadDir(dir)
|
fis, err := ioutil.ReadDir(dir)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -69,16 +73,18 @@ func ReadDir(dir string, filter func(name string) bool) (files []string) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 去掉 html tag
|
||||||
func IgnoreHtmlTag(src string) string {
|
func IgnoreHtmlTag(src string) string {
|
||||||
//去除所有尖括号内的HTML代码
|
// 去除所有尖括号内的HTML代码
|
||||||
re, _ := regexp.Compile("\\<[\\S\\s]+?\\>")
|
re, _ := regexp.Compile(`<[\S\s]+?>`)
|
||||||
src = re.ReplaceAllString(src, "")
|
src = re.ReplaceAllString(src, "")
|
||||||
|
|
||||||
//去除换行符
|
// 去除换行符
|
||||||
re, _ = regexp.Compile("\\s{2,}")
|
re, _ = regexp.Compile(`\s+`)
|
||||||
return re.ReplaceAllString(src, "")
|
return re.ReplaceAllString(src, "")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 获取第一张图片
|
||||||
func PickFirstImage(html string) string {
|
func PickFirstImage(html string) string {
|
||||||
re, _ := regexp.Compile(`data-src="(.*?)"`)
|
re, _ := regexp.Compile(`data-src="(.*?)"`)
|
||||||
sli := re.FindAllStringSubmatch(html, 1)
|
sli := re.FindAllStringSubmatch(html, 1)
|
||||||
@@ -98,15 +104,16 @@ const (
|
|||||||
YEARS_AGO = "%d年前"
|
YEARS_AGO = "%d年前"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// 时间转换为间隔
|
||||||
func ConvertStr(str string) string {
|
func ConvertStr(str string) string {
|
||||||
t, err := time.ParseInLocation("2006-01-02T15:04:05", str, time.UTC)
|
t, err := time.ParseInLocation("2006-01-02T15:04:05", str, time.UTC)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logd.Error(err, str)
|
logd.Error(err, str)
|
||||||
return JUST_NOW
|
return JUST_NOW
|
||||||
}
|
}
|
||||||
now := time.Now()
|
now := time.Now().UTC()
|
||||||
y1, m1, d1 := t.Date()
|
y1, m1, d1 := t.Date()
|
||||||
y2, m2, d2 := now.UTC().Date()
|
y2, m2, d2 := now.Date()
|
||||||
h1, mi1, s1 := t.Clock()
|
h1, mi1, s1 := t.Clock()
|
||||||
h2, mi2, s2 := now.Clock()
|
h2, mi2, s2 := now.Clock()
|
||||||
if y := y2 - y1; y > 1 || (y == 1 && m2-m1 >= 0) {
|
if y := y2 - y1; y > 1 || (y == 1 && m2-m1 >= 0) {
|
||||||
@@ -123,6 +130,7 @@ func ConvertStr(str string) string {
|
|||||||
return JUST_NOW
|
return JUST_NOW
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 获取天数
|
||||||
func dayIn(year int, m time.Month) int {
|
func dayIn(year int, m time.Month) int {
|
||||||
if m == time.February && isLeap(year) {
|
if m == time.February && isLeap(year) {
|
||||||
return 29
|
return 29
|
||||||
@@ -130,6 +138,7 @@ func dayIn(year int, m time.Month) int {
|
|||||||
return monthToDays[m]
|
return monthToDays[m]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 是否是闰年
|
||||||
func isLeap(year int) bool {
|
func isLeap(year int) bool {
|
||||||
return year%4 == 0 && (year%100 != 0 || year%400 == 0)
|
return year%4 == 0 && (year%100 != 0 || year%400 == 0)
|
||||||
}
|
}
|
||||||
|
|||||||
4
ping.go
4
ping.go
@@ -3,6 +3,7 @@ package main
|
|||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"encoding/xml"
|
"encoding/xml"
|
||||||
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
@@ -64,7 +65,7 @@ func (p *pingRPC) PingFunc(slug string) {
|
|||||||
if len(setting.Conf.PingRPCs) == 0 {
|
if len(setting.Conf.PingRPCs) == 0 {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
p.Params.Param[1].Value = "https://" + setting.Conf.Mode.Domain + "/post/" + slug + ".html"
|
p.Params.Param[1].Value = fmt.Sprintf("https://%s/post/%s.html", setting.Conf.Mode.Domain, slug)
|
||||||
buf := &bytes.Buffer{}
|
buf := &bytes.Buffer{}
|
||||||
buf.WriteString(xml.Header)
|
buf.WriteString(xml.Header)
|
||||||
enc := xml.NewEncoder(buf)
|
enc := xml.NewEncoder(buf)
|
||||||
@@ -105,6 +106,7 @@ func init() {
|
|||||||
Pings = append(Pings, pr)
|
Pings = append(Pings, pr)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ping
|
||||||
func DoPings(slug string) {
|
func DoPings(slug string) {
|
||||||
for _, p := range Pings {
|
for _, p := range Pings {
|
||||||
go p.PingFunc(slug)
|
go p.PingFunc(slug)
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ func TestPingRPC(t *testing.T) {
|
|||||||
}
|
}
|
||||||
pr.Params.Param = [4]rpcValue{
|
pr.Params.Param = [4]rpcValue{
|
||||||
rpcValue{Value: Ei.BTitle},
|
rpcValue{Value: Ei.BTitle},
|
||||||
rpcValue{Value: "https://" + setting.Conf.Mode.Domains[0]},
|
rpcValue{Value: "https://" + setting.Conf.Mode.Domain},
|
||||||
rpcValue{Value: "https://deepzz.com/post/gdb-debug.html"},
|
rpcValue{Value: "https://deepzz.com/post/gdb-debug.html"},
|
||||||
rpcValue{Value: "https://deepzz.com/rss.html"},
|
rpcValue{Value: "https://deepzz.com/rss.html"},
|
||||||
}
|
}
|
||||||
|
|||||||
92
qiniu.go
92
qiniu.go
@@ -7,94 +7,77 @@ import (
|
|||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
|
||||||
"github.com/eiblog/eiblog/setting"
|
"github.com/eiblog/eiblog/setting"
|
||||||
"qiniupkg.com/api.v7/kodo"
|
"github.com/qiniu/api.v7/auth/qbox"
|
||||||
"qiniupkg.com/api.v7/kodocli"
|
"github.com/qiniu/api.v7/storage"
|
||||||
url "qiniupkg.com/x/url.v7"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var qiniu_cfg = &kodo.Config{
|
// 进度条
|
||||||
AccessKey: setting.Conf.Kodo.AccessKey,
|
|
||||||
SecretKey: setting.Conf.Kodo.SecretKey,
|
|
||||||
Scheme: "https",
|
|
||||||
}
|
|
||||||
|
|
||||||
type bucket struct {
|
|
||||||
name string
|
|
||||||
domain string
|
|
||||||
accessKey string
|
|
||||||
secretKey string
|
|
||||||
}
|
|
||||||
|
|
||||||
type PutRet struct {
|
|
||||||
Hash string `json:"hash"`
|
|
||||||
Key string `json:"key"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func onProgress(fsize, uploaded int64) {
|
func onProgress(fsize, uploaded int64) {
|
||||||
d := int(float64(uploaded) / float64(fsize) * 100)
|
d := int(float64(uploaded) / float64(fsize) * 100)
|
||||||
if fsize == uploaded {
|
if fsize == uploaded {
|
||||||
fmt.Printf("\rUpload completed! ")
|
fmt.Printf("\rUpload completed! \n")
|
||||||
} else {
|
} else {
|
||||||
fmt.Printf("\r%02d%% uploaded ", int(d))
|
fmt.Printf("\r%02d%% uploaded ", int(d))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 上传文件
|
||||||
func FileUpload(name string, size int64, data io.Reader) (string, error) {
|
func FileUpload(name string, size int64, data io.Reader) (string, error) {
|
||||||
if setting.Conf.Kodo.AccessKey == "" || setting.Conf.Kodo.SecretKey == "" {
|
if setting.Conf.Qiniu.AccessKey == "" || setting.Conf.Qiniu.SecretKey == "" {
|
||||||
return "", errors.New("qiniu config error")
|
return "", errors.New("qiniu config error")
|
||||||
}
|
}
|
||||||
|
|
||||||
// 创建一个client
|
key := getKey(name)
|
||||||
c := kodo.New(0, qiniu_cfg)
|
mac := qbox.NewMac(setting.Conf.Qiniu.AccessKey, setting.Conf.Qiniu.SecretKey)
|
||||||
|
|
||||||
// 设置上传的策略
|
// 设置上传的策略
|
||||||
policy := &kodo.PutPolicy{
|
putPolicy := &storage.PutPolicy{
|
||||||
Scope: setting.Conf.Kodo.Name,
|
Scope: setting.Conf.Qiniu.Bucket,
|
||||||
Expires: 3600,
|
Expires: 3600,
|
||||||
InsertOnly: 1,
|
InsertOnly: 1,
|
||||||
}
|
}
|
||||||
|
// 上传token
|
||||||
|
upToken := putPolicy.UploadToken(mac)
|
||||||
|
|
||||||
// 生成一个上传token
|
// 上传配置
|
||||||
token := c.MakeUptoken(policy)
|
cfg := &storage.Config{
|
||||||
// 构建一个uploader
|
Zone: &storage.ZoneHuadong,
|
||||||
zone := 0
|
UseHTTPS: true,
|
||||||
uploader := kodocli.NewUploader(zone, nil)
|
|
||||||
|
|
||||||
key := getKey(name)
|
|
||||||
if key == "" {
|
|
||||||
return "", errors.New("不支持的文件类型")
|
|
||||||
}
|
}
|
||||||
|
// uploader
|
||||||
|
uploader := storage.NewFormUploader(cfg)
|
||||||
|
ret := new(storage.PutRet)
|
||||||
|
putExtra := &storage.PutExtra{}
|
||||||
|
|
||||||
var ret PutRet
|
err := uploader.Put(nil, ret, upToken, key, data, size, putExtra)
|
||||||
var extra = kodocli.PutExtra{OnProgress: onProgress}
|
|
||||||
err := uploader.Put(nil, &ret, token, key, data, size, &extra)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
url := "https://" + setting.Conf.Kodo.Domain + "/" + url.Escape(key)
|
url := "https://" + setting.Conf.Qiniu.Domain + "/" + key
|
||||||
return url, nil
|
return url, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 删除文件
|
||||||
func FileDelete(name string) error {
|
func FileDelete(name string) error {
|
||||||
// new一个Bucket管理对象
|
|
||||||
c := kodo.New(0, qiniu_cfg)
|
|
||||||
p := c.Bucket(setting.Conf.Kodo.Name)
|
|
||||||
|
|
||||||
key := getKey(name)
|
key := getKey(name)
|
||||||
if key == "" {
|
|
||||||
return errors.New("不支持的文件类型")
|
|
||||||
}
|
|
||||||
|
|
||||||
// 调用Delete方法删除文件
|
mac := qbox.NewMac(setting.Conf.Qiniu.AccessKey, setting.Conf.Qiniu.SecretKey)
|
||||||
err := p.Delete(nil, key)
|
// 上传配置
|
||||||
// 打印返回值以及出错信息
|
cfg := &storage.Config{
|
||||||
|
Zone: &storage.ZoneHuadong,
|
||||||
|
UseHTTPS: true,
|
||||||
|
}
|
||||||
|
// manager
|
||||||
|
bucketManager := storage.NewBucketManager(mac, cfg)
|
||||||
|
// Delete
|
||||||
|
err := bucketManager.Delete(setting.Conf.Qiniu.Bucket, key)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 修复路径
|
||||||
func getKey(name string) string {
|
func getKey(name string) string {
|
||||||
ext := filepath.Ext(name)
|
ext := filepath.Ext(name)
|
||||||
var key string
|
var key string
|
||||||
@@ -103,9 +86,12 @@ func getKey(name string) string {
|
|||||||
key = "blog/img/" + name
|
key = "blog/img/" + name
|
||||||
case ".mov", ".mp4":
|
case ".mov", ".mp4":
|
||||||
key = "blog/video/" + name
|
key = "blog/video/" + name
|
||||||
case ".go", ".js", ".css", ".cpp", ".php", ".rb", ".java", ".py", ".sql", ".lua", ".html", ".sh", ".xml", ".cs":
|
case ".go", ".js", ".css", ".cpp", ".php", ".rb",
|
||||||
|
".java", ".py", ".sql", ".lua", ".html",
|
||||||
|
".sh", ".xml", ".cs":
|
||||||
key = "blog/code/" + name
|
key = "blog/code/" + name
|
||||||
case ".txt", ".md", ".ini", ".yaml", ".yml", ".doc", ".ppt", ".pdf":
|
case ".txt", ".md", ".ini", ".yaml", ".yml",
|
||||||
|
".doc", ".ppt", ".pdf":
|
||||||
key = "blog/document/" + name
|
key = "blog/document/" + name
|
||||||
case ".zip", ".rar", ".tar", ".gz":
|
case ".zip", ".rar", ".tar", ".gz":
|
||||||
key = "blog/archive/" + name
|
key = "blog/archive/" + name
|
||||||
|
|||||||
@@ -7,11 +7,13 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func TestUpload(t *testing.T) {
|
func TestUpload(t *testing.T) {
|
||||||
path := "/Users/chen/Desktop/png-MicroService-by-StuQ.png"
|
path := "qiniu.go"
|
||||||
file, err := os.Open(path)
|
file, err := os.Open(path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
defer file.Close()
|
||||||
|
|
||||||
info, _ := file.Stat()
|
info, _ := file.Stat()
|
||||||
url, err := FileUpload(info.Name(), info.Size(), file)
|
url, err := FileUpload(info.Name(), info.Size(), file)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
15
router.go
15
router.go
@@ -2,8 +2,9 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"crypto/rand"
|
||||||
"fmt"
|
"fmt"
|
||||||
"html/template"
|
"text/template"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/eiblog/eiblog/setting"
|
"github.com/eiblog/eiblog/setting"
|
||||||
@@ -20,12 +21,19 @@ var (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
|
// 运行模式
|
||||||
if setting.Conf.RunMode == setting.PROD {
|
if setting.Conf.RunMode == setting.PROD {
|
||||||
gin.SetMode(gin.ReleaseMode)
|
gin.SetMode(gin.ReleaseMode)
|
||||||
logd.SetLevel(logd.Lerror)
|
logd.SetLevel(logd.Lerror)
|
||||||
}
|
}
|
||||||
|
|
||||||
router = gin.Default()
|
router = gin.Default()
|
||||||
store := sessions.NewCookieStore([]byte("eiblog321"))
|
b := make([]byte, 16)
|
||||||
|
_, err := rand.Read(b)
|
||||||
|
if err != nil {
|
||||||
|
logd.Fatal(err)
|
||||||
|
}
|
||||||
|
store := sessions.NewCookieStore(b)
|
||||||
store.Options(sessions.Options{
|
store.Options(sessions.Options{
|
||||||
MaxAge: 86400 * 7,
|
MaxAge: 86400 * 7,
|
||||||
Path: "/",
|
Path: "/",
|
||||||
@@ -41,7 +49,7 @@ func init() {
|
|||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
})
|
})
|
||||||
_, err := Tmpl.ParseFiles(files...)
|
_, err = Tmpl.ParseFiles(files...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logd.Fatal(err)
|
logd.Fatal(err)
|
||||||
}
|
}
|
||||||
@@ -90,6 +98,7 @@ func init() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 开始运行
|
||||||
func Run() {
|
func Run() {
|
||||||
var (
|
var (
|
||||||
endRunning = make(chan bool, 1)
|
endRunning = make(chan bool, 1)
|
||||||
|
|||||||
@@ -35,15 +35,16 @@ type Config struct {
|
|||||||
Clean int // 清理回收箱频率
|
Clean int // 清理回收箱频率
|
||||||
}
|
}
|
||||||
Disqus struct { // 获取文章数量相关
|
Disqus struct { // 获取文章数量相关
|
||||||
ShortName string
|
ShortName string
|
||||||
PublicKey string
|
PublicKey string
|
||||||
AccessToken string
|
AccessToken string
|
||||||
PostsCount string
|
PostsCount string
|
||||||
PostsList string
|
PostsList string
|
||||||
PostCreate string
|
PostCreate string
|
||||||
PostApprove string
|
PostApprove string
|
||||||
Embed string
|
ThreadCreate string
|
||||||
Interval int
|
Embed string
|
||||||
|
Interval int
|
||||||
}
|
}
|
||||||
Google struct { // 谷歌统计
|
Google struct { // 谷歌统计
|
||||||
URL string
|
URL string
|
||||||
@@ -51,8 +52,8 @@ type Config struct {
|
|||||||
V string
|
V string
|
||||||
T string
|
T string
|
||||||
}
|
}
|
||||||
Kodo struct { // 七牛CDN
|
Qiniu struct { // 七牛CDN
|
||||||
Name string
|
Bucket string
|
||||||
Domain string
|
Domain string
|
||||||
AccessKey string
|
AccessKey string
|
||||||
SecretKey string
|
SecretKey string
|
||||||
|
|||||||
12
vendor/github.com/deepzz0/logd/logd.go
generated
vendored
12
vendor/github.com/deepzz0/logd/logd.go
generated
vendored
@@ -67,10 +67,20 @@ type LogOption struct {
|
|||||||
Mails Emailer // 告警邮件
|
Mails Emailer // 告警邮件
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func osSep() string {
|
||||||
|
var sep string
|
||||||
|
if os.IsPathSeparator('\\') {
|
||||||
|
sep = "\\"
|
||||||
|
} else {
|
||||||
|
sep = "/"
|
||||||
|
}
|
||||||
|
return sep
|
||||||
|
}
|
||||||
|
|
||||||
// 新建日志打印器
|
// 新建日志打印器
|
||||||
func New(option LogOption) *Logger {
|
func New(option LogOption) *Logger {
|
||||||
wd, _ := os.Getwd()
|
wd, _ := os.Getwd()
|
||||||
index := strings.LastIndex(wd, "/")
|
index := strings.LastIndex(wd, osSep())
|
||||||
logger := &Logger{
|
logger := &Logger{
|
||||||
obj: wd[index+1:],
|
obj: wd[index+1:],
|
||||||
out: option.Out,
|
out: option.Out,
|
||||||
|
|||||||
1
vendor/github.com/garyburd/redigo/.travis.yml
generated
vendored
1
vendor/github.com/garyburd/redigo/.travis.yml
generated
vendored
@@ -9,6 +9,7 @@ go:
|
|||||||
- 1.6
|
- 1.6
|
||||||
- 1.7
|
- 1.7
|
||||||
- 1.8
|
- 1.8
|
||||||
|
- 1.9
|
||||||
- tip
|
- tip
|
||||||
|
|
||||||
script:
|
script:
|
||||||
|
|||||||
1
vendor/github.com/garyburd/redigo/README.markdown
generated
vendored
1
vendor/github.com/garyburd/redigo/README.markdown
generated
vendored
@@ -21,6 +21,7 @@ Documentation
|
|||||||
|
|
||||||
- [API Reference](http://godoc.org/github.com/garyburd/redigo/redis)
|
- [API Reference](http://godoc.org/github.com/garyburd/redigo/redis)
|
||||||
- [FAQ](https://github.com/garyburd/redigo/wiki/FAQ)
|
- [FAQ](https://github.com/garyburd/redigo/wiki/FAQ)
|
||||||
|
- [Examples](https://godoc.org/github.com/garyburd/redigo/redis#pkg-examples)
|
||||||
|
|
||||||
Installation
|
Installation
|
||||||
------------
|
------------
|
||||||
|
|||||||
165
vendor/github.com/garyburd/redigo/redis/conn.go
generated
vendored
165
vendor/github.com/garyburd/redigo/redis/conn.go
generated
vendored
@@ -29,9 +29,12 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
_ ConnWithTimeout = (*conn)(nil)
|
||||||
|
)
|
||||||
|
|
||||||
// conn is the low-level implementation of Conn
|
// conn is the low-level implementation of Conn
|
||||||
type conn struct {
|
type conn struct {
|
||||||
|
|
||||||
// Shared
|
// Shared
|
||||||
mu sync.Mutex
|
mu sync.Mutex
|
||||||
pending int
|
pending int
|
||||||
@@ -73,10 +76,11 @@ type DialOption struct {
|
|||||||
type dialOptions struct {
|
type dialOptions struct {
|
||||||
readTimeout time.Duration
|
readTimeout time.Duration
|
||||||
writeTimeout time.Duration
|
writeTimeout time.Duration
|
||||||
|
dialer *net.Dialer
|
||||||
dial func(network, addr string) (net.Conn, error)
|
dial func(network, addr string) (net.Conn, error)
|
||||||
db int
|
db int
|
||||||
password string
|
password string
|
||||||
dialTLS bool
|
useTLS bool
|
||||||
skipVerify bool
|
skipVerify bool
|
||||||
tlsConfig *tls.Config
|
tlsConfig *tls.Config
|
||||||
}
|
}
|
||||||
@@ -95,17 +99,27 @@ func DialWriteTimeout(d time.Duration) DialOption {
|
|||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
// DialConnectTimeout specifies the timeout for connecting to the Redis server.
|
// DialConnectTimeout specifies the timeout for connecting to the Redis server when
|
||||||
|
// no DialNetDial option is specified.
|
||||||
func DialConnectTimeout(d time.Duration) DialOption {
|
func DialConnectTimeout(d time.Duration) DialOption {
|
||||||
return DialOption{func(do *dialOptions) {
|
return DialOption{func(do *dialOptions) {
|
||||||
dialer := net.Dialer{Timeout: d}
|
do.dialer.Timeout = d
|
||||||
do.dial = dialer.Dial
|
}}
|
||||||
|
}
|
||||||
|
|
||||||
|
// DialKeepAlive specifies the keep-alive period for TCP connections to the Redis server
|
||||||
|
// when no DialNetDial option is specified.
|
||||||
|
// If zero, keep-alives are not enabled. If no DialKeepAlive option is specified then
|
||||||
|
// the default of 5 minutes is used to ensure that half-closed TCP sessions are detected.
|
||||||
|
func DialKeepAlive(d time.Duration) DialOption {
|
||||||
|
return DialOption{func(do *dialOptions) {
|
||||||
|
do.dialer.KeepAlive = d
|
||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
// DialNetDial specifies a custom dial function for creating TCP
|
// DialNetDial specifies a custom dial function for creating TCP
|
||||||
// connections. If this option is left out, then net.Dial is
|
// connections, otherwise a net.Dialer customized via the other options is used.
|
||||||
// used. DialNetDial overrides DialConnectTimeout.
|
// DialNetDial overrides DialConnectTimeout and DialKeepAlive.
|
||||||
func DialNetDial(dial func(network, addr string) (net.Conn, error)) DialOption {
|
func DialNetDial(dial func(network, addr string) (net.Conn, error)) DialOption {
|
||||||
return DialOption{func(do *dialOptions) {
|
return DialOption{func(do *dialOptions) {
|
||||||
do.dial = dial
|
do.dial = dial
|
||||||
@@ -135,31 +149,49 @@ func DialTLSConfig(c *tls.Config) DialOption {
|
|||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
// DialTLSSkipVerify to disable server name verification when connecting
|
// DialTLSSkipVerify disables server name verification when connecting over
|
||||||
// over TLS. Has no effect when not dialing a TLS connection.
|
// TLS. Has no effect when not dialing a TLS connection.
|
||||||
func DialTLSSkipVerify(skip bool) DialOption {
|
func DialTLSSkipVerify(skip bool) DialOption {
|
||||||
return DialOption{func(do *dialOptions) {
|
return DialOption{func(do *dialOptions) {
|
||||||
do.skipVerify = skip
|
do.skipVerify = skip
|
||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DialUseTLS specifies whether TLS should be used when connecting to the
|
||||||
|
// server. This option is ignore by DialURL.
|
||||||
|
func DialUseTLS(useTLS bool) DialOption {
|
||||||
|
return DialOption{func(do *dialOptions) {
|
||||||
|
do.useTLS = useTLS
|
||||||
|
}}
|
||||||
|
}
|
||||||
|
|
||||||
// Dial connects to the Redis server at the given network and
|
// Dial connects to the Redis server at the given network and
|
||||||
// address using the specified options.
|
// address using the specified options.
|
||||||
func Dial(network, address string, options ...DialOption) (Conn, error) {
|
func Dial(network, address string, options ...DialOption) (Conn, error) {
|
||||||
do := dialOptions{
|
do := dialOptions{
|
||||||
dial: net.Dial,
|
dialer: &net.Dialer{
|
||||||
|
KeepAlive: time.Minute * 5,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
for _, option := range options {
|
for _, option := range options {
|
||||||
option.f(&do)
|
option.f(&do)
|
||||||
}
|
}
|
||||||
|
if do.dial == nil {
|
||||||
|
do.dial = do.dialer.Dial
|
||||||
|
}
|
||||||
|
|
||||||
netConn, err := do.dial(network, address)
|
netConn, err := do.dial(network, address)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if do.dialTLS {
|
if do.useTLS {
|
||||||
tlsConfig := cloneTLSClientConfig(do.tlsConfig, do.skipVerify)
|
var tlsConfig *tls.Config
|
||||||
|
if do.tlsConfig == nil {
|
||||||
|
tlsConfig = &tls.Config{InsecureSkipVerify: do.skipVerify}
|
||||||
|
} else {
|
||||||
|
tlsConfig = cloneTLSConfig(do.tlsConfig)
|
||||||
|
}
|
||||||
if tlsConfig.ServerName == "" {
|
if tlsConfig.ServerName == "" {
|
||||||
host, _, err := net.SplitHostPort(address)
|
host, _, err := net.SplitHostPort(address)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -202,10 +234,6 @@ func Dial(network, address string, options ...DialOption) (Conn, error) {
|
|||||||
return c, nil
|
return c, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func dialTLS(do *dialOptions) {
|
|
||||||
do.dialTLS = true
|
|
||||||
}
|
|
||||||
|
|
||||||
var pathDBRegexp = regexp.MustCompile(`/(\d*)\z`)
|
var pathDBRegexp = regexp.MustCompile(`/(\d*)\z`)
|
||||||
|
|
||||||
// DialURL connects to a Redis server at the given URL using the Redis
|
// DialURL connects to a Redis server at the given URL using the Redis
|
||||||
@@ -257,9 +285,7 @@ func DialURL(rawurl string, options ...DialOption) (Conn, error) {
|
|||||||
return nil, fmt.Errorf("invalid database: %s", u.Path[1:])
|
return nil, fmt.Errorf("invalid database: %s", u.Path[1:])
|
||||||
}
|
}
|
||||||
|
|
||||||
if u.Scheme == "rediss" {
|
options = append(options, DialUseTLS(u.Scheme == "rediss"))
|
||||||
options = append([]DialOption{{dialTLS}}, options...)
|
|
||||||
}
|
|
||||||
|
|
||||||
return Dial("tcp", address, options...)
|
return Dial("tcp", address, options...)
|
||||||
}
|
}
|
||||||
@@ -344,43 +370,55 @@ func (c *conn) writeFloat64(n float64) error {
|
|||||||
return c.writeBytes(strconv.AppendFloat(c.numScratch[:0], n, 'g', -1, 64))
|
return c.writeBytes(strconv.AppendFloat(c.numScratch[:0], n, 'g', -1, 64))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *conn) writeCommand(cmd string, args []interface{}) (err error) {
|
func (c *conn) writeCommand(cmd string, args []interface{}) error {
|
||||||
c.writeLen('*', 1+len(args))
|
c.writeLen('*', 1+len(args))
|
||||||
err = c.writeString(cmd)
|
if err := c.writeString(cmd); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
for _, arg := range args {
|
for _, arg := range args {
|
||||||
if err != nil {
|
if err := c.writeArg(arg, true); err != nil {
|
||||||
break
|
return err
|
||||||
}
|
|
||||||
switch arg := arg.(type) {
|
|
||||||
case string:
|
|
||||||
err = c.writeString(arg)
|
|
||||||
case []byte:
|
|
||||||
err = c.writeBytes(arg)
|
|
||||||
case int:
|
|
||||||
err = c.writeInt64(int64(arg))
|
|
||||||
case int64:
|
|
||||||
err = c.writeInt64(arg)
|
|
||||||
case float64:
|
|
||||||
err = c.writeFloat64(arg)
|
|
||||||
case bool:
|
|
||||||
if arg {
|
|
||||||
err = c.writeString("1")
|
|
||||||
} else {
|
|
||||||
err = c.writeString("0")
|
|
||||||
}
|
|
||||||
case nil:
|
|
||||||
err = c.writeString("")
|
|
||||||
case Argument:
|
|
||||||
var buf bytes.Buffer
|
|
||||||
fmt.Fprint(&buf, arg.RedisArg())
|
|
||||||
err = c.writeBytes(buf.Bytes())
|
|
||||||
default:
|
|
||||||
var buf bytes.Buffer
|
|
||||||
fmt.Fprint(&buf, arg)
|
|
||||||
err = c.writeBytes(buf.Bytes())
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return err
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *conn) writeArg(arg interface{}, argumentTypeOK bool) (err error) {
|
||||||
|
switch arg := arg.(type) {
|
||||||
|
case string:
|
||||||
|
return c.writeString(arg)
|
||||||
|
case []byte:
|
||||||
|
return c.writeBytes(arg)
|
||||||
|
case int:
|
||||||
|
return c.writeInt64(int64(arg))
|
||||||
|
case int64:
|
||||||
|
return c.writeInt64(arg)
|
||||||
|
case float64:
|
||||||
|
return c.writeFloat64(arg)
|
||||||
|
case bool:
|
||||||
|
if arg {
|
||||||
|
return c.writeString("1")
|
||||||
|
} else {
|
||||||
|
return c.writeString("0")
|
||||||
|
}
|
||||||
|
case nil:
|
||||||
|
return c.writeString("")
|
||||||
|
case Argument:
|
||||||
|
if argumentTypeOK {
|
||||||
|
return c.writeArg(arg.RedisArg(), false)
|
||||||
|
}
|
||||||
|
// See comment in default clause below.
|
||||||
|
var buf bytes.Buffer
|
||||||
|
fmt.Fprint(&buf, arg)
|
||||||
|
return c.writeBytes(buf.Bytes())
|
||||||
|
default:
|
||||||
|
// This default clause is intended to handle builtin numeric types.
|
||||||
|
// The function should return an error for other types, but this is not
|
||||||
|
// done for compatibility with previous versions of the package.
|
||||||
|
var buf bytes.Buffer
|
||||||
|
fmt.Fprint(&buf, arg)
|
||||||
|
return c.writeBytes(buf.Bytes())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type protocolError string
|
type protocolError string
|
||||||
@@ -542,10 +580,17 @@ func (c *conn) Flush() error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *conn) Receive() (reply interface{}, err error) {
|
func (c *conn) Receive() (interface{}, error) {
|
||||||
if c.readTimeout != 0 {
|
return c.ReceiveWithTimeout(c.readTimeout)
|
||||||
c.conn.SetReadDeadline(time.Now().Add(c.readTimeout))
|
}
|
||||||
|
|
||||||
|
func (c *conn) ReceiveWithTimeout(timeout time.Duration) (reply interface{}, err error) {
|
||||||
|
var deadline time.Time
|
||||||
|
if timeout != 0 {
|
||||||
|
deadline = time.Now().Add(timeout)
|
||||||
}
|
}
|
||||||
|
c.conn.SetReadDeadline(deadline)
|
||||||
|
|
||||||
if reply, err = c.readReply(); err != nil {
|
if reply, err = c.readReply(); err != nil {
|
||||||
return nil, c.fatal(err)
|
return nil, c.fatal(err)
|
||||||
}
|
}
|
||||||
@@ -568,6 +613,10 @@ func (c *conn) Receive() (reply interface{}, err error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *conn) Do(cmd string, args ...interface{}) (interface{}, error) {
|
func (c *conn) Do(cmd string, args ...interface{}) (interface{}, error) {
|
||||||
|
return c.DoWithTimeout(c.readTimeout, cmd, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *conn) DoWithTimeout(readTimeout time.Duration, cmd string, args ...interface{}) (interface{}, error) {
|
||||||
c.mu.Lock()
|
c.mu.Lock()
|
||||||
pending := c.pending
|
pending := c.pending
|
||||||
c.pending = 0
|
c.pending = 0
|
||||||
@@ -591,9 +640,11 @@ func (c *conn) Do(cmd string, args ...interface{}) (interface{}, error) {
|
|||||||
return nil, c.fatal(err)
|
return nil, c.fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if c.readTimeout != 0 {
|
var deadline time.Time
|
||||||
c.conn.SetReadDeadline(time.Now().Add(c.readTimeout))
|
if readTimeout != 0 {
|
||||||
|
deadline = time.Now().Add(readTimeout)
|
||||||
}
|
}
|
||||||
|
c.conn.SetReadDeadline(deadline)
|
||||||
|
|
||||||
if cmd == "" {
|
if cmd == "" {
|
||||||
reply := make([]interface{}, pending)
|
reply := make([]interface{}, pending)
|
||||||
|
|||||||
257
vendor/github.com/garyburd/redigo/redis/conn_test.go
generated
vendored
257
vendor/github.com/garyburd/redigo/redis/conn_test.go
generated
vendored
@@ -16,6 +16,9 @@ package redis_test
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"crypto/tls"
|
||||||
|
"crypto/x509"
|
||||||
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"math"
|
"math"
|
||||||
"net"
|
"net"
|
||||||
@@ -31,18 +34,45 @@ import (
|
|||||||
type testConn struct {
|
type testConn struct {
|
||||||
io.Reader
|
io.Reader
|
||||||
io.Writer
|
io.Writer
|
||||||
|
readDeadline time.Time
|
||||||
|
writeDeadline time.Time
|
||||||
}
|
}
|
||||||
|
|
||||||
func (*testConn) Close() error { return nil }
|
func (*testConn) Close() error { return nil }
|
||||||
func (*testConn) LocalAddr() net.Addr { return nil }
|
func (*testConn) LocalAddr() net.Addr { return nil }
|
||||||
func (*testConn) RemoteAddr() net.Addr { return nil }
|
func (*testConn) RemoteAddr() net.Addr { return nil }
|
||||||
func (*testConn) SetDeadline(t time.Time) error { return nil }
|
func (c *testConn) SetDeadline(t time.Time) error { c.readDeadline = t; c.writeDeadline = t; return nil }
|
||||||
func (*testConn) SetReadDeadline(t time.Time) error { return nil }
|
func (c *testConn) SetReadDeadline(t time.Time) error { c.readDeadline = t; return nil }
|
||||||
func (*testConn) SetWriteDeadline(t time.Time) error { return nil }
|
func (c *testConn) SetWriteDeadline(t time.Time) error { c.writeDeadline = t; return nil }
|
||||||
|
|
||||||
func dialTestConn(r io.Reader, w io.Writer) redis.DialOption {
|
func dialTestConn(r string, w io.Writer) redis.DialOption {
|
||||||
return redis.DialNetDial(func(net, addr string) (net.Conn, error) {
|
return redis.DialNetDial(func(network, addr string) (net.Conn, error) {
|
||||||
return &testConn{Reader: r, Writer: w}, nil
|
return &testConn{Reader: strings.NewReader(r), Writer: w}, nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
type tlsTestConn struct {
|
||||||
|
net.Conn
|
||||||
|
done chan struct{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *tlsTestConn) Close() error {
|
||||||
|
c.Conn.Close()
|
||||||
|
<-c.done
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func dialTestConnTLS(r string, w io.Writer) redis.DialOption {
|
||||||
|
return redis.DialNetDial(func(network, addr string) (net.Conn, error) {
|
||||||
|
client, server := net.Pipe()
|
||||||
|
tlsServer := tls.Server(server, &serverTLSConfig)
|
||||||
|
go io.Copy(tlsServer, strings.NewReader(r))
|
||||||
|
done := make(chan struct{})
|
||||||
|
go func() {
|
||||||
|
io.Copy(w, tlsServer)
|
||||||
|
close(done)
|
||||||
|
}()
|
||||||
|
return &tlsTestConn{Conn: client, done: done}, nil
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -54,6 +84,10 @@ func (t durationArg) RedisArg() interface{} {
|
|||||||
return t.Seconds()
|
return t.Seconds()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type recursiveArg int
|
||||||
|
|
||||||
|
func (v recursiveArg) RedisArg() interface{} { return v }
|
||||||
|
|
||||||
var writeTests = []struct {
|
var writeTests = []struct {
|
||||||
args []interface{}
|
args []interface{}
|
||||||
expected string
|
expected string
|
||||||
@@ -94,6 +128,10 @@ var writeTests = []struct {
|
|||||||
[]interface{}{"SET", "key", durationArg{time.Minute}},
|
[]interface{}{"SET", "key", durationArg{time.Minute}},
|
||||||
"*3\r\n$3\r\nSET\r\n$3\r\nkey\r\n$2\r\n60\r\n",
|
"*3\r\n$3\r\nSET\r\n$3\r\nkey\r\n$2\r\n60\r\n",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
[]interface{}{"SET", "key", recursiveArg(123)},
|
||||||
|
"*3\r\n$3\r\nSET\r\n$3\r\nkey\r\n$3\r\n123\r\n",
|
||||||
|
},
|
||||||
{
|
{
|
||||||
[]interface{}{"ECHO", true, false},
|
[]interface{}{"ECHO", true, false},
|
||||||
"*3\r\n$4\r\nECHO\r\n$1\r\n1\r\n$1\r\n0\r\n",
|
"*3\r\n$4\r\nECHO\r\n$1\r\n1\r\n$1\r\n0\r\n",
|
||||||
@@ -103,7 +141,7 @@ var writeTests = []struct {
|
|||||||
func TestWrite(t *testing.T) {
|
func TestWrite(t *testing.T) {
|
||||||
for _, tt := range writeTests {
|
for _, tt := range writeTests {
|
||||||
var buf bytes.Buffer
|
var buf bytes.Buffer
|
||||||
c, _ := redis.Dial("", "", dialTestConn(nil, &buf))
|
c, _ := redis.Dial("", "", dialTestConn("", &buf))
|
||||||
err := c.Send(tt.args[0].(string), tt.args[1:]...)
|
err := c.Send(tt.args[0].(string), tt.args[1:]...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("Send(%v) returned error %v", tt.args, err)
|
t.Errorf("Send(%v) returned error %v", tt.args, err)
|
||||||
@@ -202,7 +240,7 @@ var readTests = []struct {
|
|||||||
|
|
||||||
func TestRead(t *testing.T) {
|
func TestRead(t *testing.T) {
|
||||||
for _, tt := range readTests {
|
for _, tt := range readTests {
|
||||||
c, _ := redis.Dial("", "", dialTestConn(strings.NewReader(tt.reply), nil))
|
c, _ := redis.Dial("", "", dialTestConn(tt.reply, nil))
|
||||||
actual, err := c.Receive()
|
actual, err := c.Receive()
|
||||||
if tt.expected == errorSentinel {
|
if tt.expected == errorSentinel {
|
||||||
if err == nil {
|
if err == nil {
|
||||||
@@ -514,41 +552,85 @@ func TestDialURLHost(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestDialURLPassword(t *testing.T) {
|
var dialURLTests = []struct {
|
||||||
var buf bytes.Buffer
|
description string
|
||||||
_, err := redis.DialURL("redis://x:abc123@localhost", dialTestConn(strings.NewReader("+OK\r\n"), &buf))
|
url string
|
||||||
if err != nil {
|
r string
|
||||||
t.Error("dial error:", err)
|
w string
|
||||||
|
}{
|
||||||
|
{"password", "redis://x:abc123@localhost", "+OK\r\n", "*2\r\n$4\r\nAUTH\r\n$6\r\nabc123\r\n"},
|
||||||
|
{"database 3", "redis://localhost/3", "+OK\r\n", "*2\r\n$6\r\nSELECT\r\n$1\r\n3\r\n"},
|
||||||
|
{"database 99", "redis://localhost/99", "+OK\r\n", "*2\r\n$6\r\nSELECT\r\n$2\r\n99\r\n"},
|
||||||
|
{"no database", "redis://localhost/", "+OK\r\n", ""},
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDialURL(t *testing.T) {
|
||||||
|
for _, tt := range dialURLTests {
|
||||||
|
var buf bytes.Buffer
|
||||||
|
// UseTLS should be ignored in all of these tests.
|
||||||
|
_, err := redis.DialURL(tt.url, dialTestConn(tt.r, &buf), redis.DialUseTLS(true))
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("%s dial error: %v", tt.description, err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if w := buf.String(); w != tt.w {
|
||||||
|
t.Errorf("%s commands = %q, want %q", tt.description, w, tt.w)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
expected := "*2\r\n$4\r\nAUTH\r\n$6\r\nabc123\r\n"
|
}
|
||||||
|
|
||||||
|
func checkPingPong(t *testing.T, buf *bytes.Buffer, c redis.Conn) {
|
||||||
|
resp, err := c.Do("PING")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal("ping error:", err)
|
||||||
|
}
|
||||||
|
// Close connection to ensure that writes to buf are complete.
|
||||||
|
c.Close()
|
||||||
|
expected := "*1\r\n$4\r\nPING\r\n"
|
||||||
actual := buf.String()
|
actual := buf.String()
|
||||||
if actual != expected {
|
if actual != expected {
|
||||||
t.Errorf("commands = %q, want %q", actual, expected)
|
t.Errorf("commands = %q, want %q", actual, expected)
|
||||||
}
|
}
|
||||||
|
if resp != "PONG" {
|
||||||
|
t.Errorf("resp = %v, want %v", resp, "PONG")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestDialURLDatabase(t *testing.T) {
|
const pingResponse = "+PONG\r\n"
|
||||||
var buf3 bytes.Buffer
|
|
||||||
_, err3 := redis.DialURL("redis://localhost/3", dialTestConn(strings.NewReader("+OK\r\n"), &buf3))
|
func TestDialURLTLS(t *testing.T) {
|
||||||
if err3 != nil {
|
var buf bytes.Buffer
|
||||||
t.Error("dial error:", err3)
|
c, err := redis.DialURL("rediss://example.com/",
|
||||||
|
redis.DialTLSConfig(&clientTLSConfig),
|
||||||
|
dialTestConnTLS(pingResponse, &buf))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal("dial error:", err)
|
||||||
}
|
}
|
||||||
expected3 := "*2\r\n$6\r\nSELECT\r\n$1\r\n3\r\n"
|
checkPingPong(t, &buf, c)
|
||||||
actual3 := buf3.String()
|
}
|
||||||
if actual3 != expected3 {
|
|
||||||
t.Errorf("commands = %q, want %q", actual3, expected3)
|
func TestDialUseTLS(t *testing.T) {
|
||||||
|
var buf bytes.Buffer
|
||||||
|
c, err := redis.Dial("tcp", "example.com:6379",
|
||||||
|
redis.DialTLSConfig(&clientTLSConfig),
|
||||||
|
dialTestConnTLS(pingResponse, &buf),
|
||||||
|
redis.DialUseTLS(true))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal("dial error:", err)
|
||||||
}
|
}
|
||||||
// empty DB means 0
|
checkPingPong(t, &buf, c)
|
||||||
var buf0 bytes.Buffer
|
}
|
||||||
_, err0 := redis.DialURL("redis://localhost/", dialTestConn(strings.NewReader("+OK\r\n"), &buf0))
|
|
||||||
if err0 != nil {
|
func TestDialTLSSKipVerify(t *testing.T) {
|
||||||
t.Error("dial error:", err0)
|
var buf bytes.Buffer
|
||||||
}
|
c, err := redis.Dial("tcp", "example.com:6379",
|
||||||
expected0 := ""
|
dialTestConnTLS(pingResponse, &buf),
|
||||||
actual0 := buf0.String()
|
redis.DialTLSSkipVerify(true),
|
||||||
if actual0 != expected0 {
|
redis.DialUseTLS(true))
|
||||||
t.Errorf("commands = %q, want %q", actual0, expected0)
|
if err != nil {
|
||||||
|
t.Fatal("dial error:", err)
|
||||||
}
|
}
|
||||||
|
checkPingPong(t, &buf, c)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Connect to local instance of Redis running on the default port.
|
// Connect to local instance of Redis running on the default port.
|
||||||
@@ -680,3 +762,106 @@ func BenchmarkDoPing(b *testing.B) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var clientTLSConfig, serverTLSConfig tls.Config
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
// The certificate and key for testing TLS dial options was created
|
||||||
|
// using the command
|
||||||
|
//
|
||||||
|
// go run GOROOT/src/crypto/tls/generate_cert.go \
|
||||||
|
// --rsa-bits 1024 \
|
||||||
|
// --host 127.0.0.1,::1,example.com --ca \
|
||||||
|
// --start-date "Jan 1 00:00:00 1970" \
|
||||||
|
// --duration=1000000h
|
||||||
|
//
|
||||||
|
// where GOROOT is the value of GOROOT reported by go env.
|
||||||
|
localhostCert := []byte(`
|
||||||
|
-----BEGIN CERTIFICATE-----
|
||||||
|
MIICFDCCAX2gAwIBAgIRAJfBL4CUxkXcdlFurb3K+iowDQYJKoZIhvcNAQELBQAw
|
||||||
|
EjEQMA4GA1UEChMHQWNtZSBDbzAgFw03MDAxMDEwMDAwMDBaGA8yMDg0MDEyOTE2
|
||||||
|
MDAwMFowEjEQMA4GA1UEChMHQWNtZSBDbzCBnzANBgkqhkiG9w0BAQEFAAOBjQAw
|
||||||
|
gYkCgYEArizw8WxMUQ3bGHLeuJ4fDrEpy+L2pqrbYRlKk1DasJ/VkB8bImzIpe6+
|
||||||
|
LGjiYIxvnDCOJ3f3QplcQuiuMyl6f2irJlJsbFT8Lo/3obnuTKAIaqUdJUqBg6y+
|
||||||
|
JaL8Auk97FvunfKFv8U1AIhgiLzAfQ/3Eaq1yi87Ra6pMjGbTtcCAwEAAaNoMGYw
|
||||||
|
DgYDVR0PAQH/BAQDAgKkMBMGA1UdJQQMMAoGCCsGAQUFBwMBMA8GA1UdEwEB/wQF
|
||||||
|
MAMBAf8wLgYDVR0RBCcwJYILZXhhbXBsZS5jb22HBH8AAAGHEAAAAAAAAAAAAAAA
|
||||||
|
AAAAAAEwDQYJKoZIhvcNAQELBQADgYEAdZ8daIVkyhVwflt5I19m0oq1TycbGO1+
|
||||||
|
ach7T6cZiBQeNR/SJtxr/wKPEpmvUgbv2BfFrKJ8QoIHYsbNSURTWSEa02pfw4k9
|
||||||
|
6RQhij3ZkG79Ituj5OYRORV6Z0HUW32r670BtcuHuAhq7YA6Nxy4FtSt7bAlVdRt
|
||||||
|
rrKgNsltzMk=
|
||||||
|
-----END CERTIFICATE-----`)
|
||||||
|
|
||||||
|
localhostKey := []byte(`
|
||||||
|
-----BEGIN RSA PRIVATE KEY-----
|
||||||
|
MIICXAIBAAKBgQCuLPDxbExRDdsYct64nh8OsSnL4vamqtthGUqTUNqwn9WQHxsi
|
||||||
|
bMil7r4saOJgjG+cMI4nd/dCmVxC6K4zKXp/aKsmUmxsVPwuj/ehue5MoAhqpR0l
|
||||||
|
SoGDrL4lovwC6T3sW+6d8oW/xTUAiGCIvMB9D/cRqrXKLztFrqkyMZtO1wIDAQAB
|
||||||
|
AoGACrc5G6FOEK6JjDeE/Fa+EmlT6PdNtXNNi+vCas3Opo8u1G8VfEi1D4BgstrB
|
||||||
|
Eq+RLkrOdB8tVyuYQYWPMhabMqF+hhKJN72j0OwfuPlVvTInwb/cKjo/zbH1IA+Y
|
||||||
|
HenHNK4ywv7/p/9/MvQPJ3I32cQBCgGUW5chVSH5M1sj5gECQQDabQAI1X0uDqCm
|
||||||
|
KbX9gXVkAgxkFddrt6LBHt57xujFcqEKFE7nwKhDh7DweVs/VEJ+kpid4z+UnLOw
|
||||||
|
KjtP9JolAkEAzCNBphQ//IsbH5rNs10wIUw3Ks/Oepicvr6kUFbIv+neRzi1iJHa
|
||||||
|
m6H7EayK3PWgax6BAsR/t0Jc9XV7r2muSwJAVzN09BHnK+ADGtNEKLTqXMbEk6B0
|
||||||
|
pDhn7ZmZUOkUPN+Kky+QYM11X6Bob1jDqQDGmymDbGUxGO+GfSofC8inUQJAGfci
|
||||||
|
Eo3g1a6b9JksMPRZeuLG4ZstGErxJRH6tH1Va5PDwitka8qhk8o2tTjNMO3NSdLH
|
||||||
|
diKoXBcE2/Pll5pJoQJBAIMiiMIzXJhnN4mX8may44J/HvMlMf2xuVH2gNMwmZuc
|
||||||
|
Bjqn3yoLHaoZVvbWOi0C2TCN4FjXjaLNZGifQPbIcaA=
|
||||||
|
-----END RSA PRIVATE KEY-----`)
|
||||||
|
|
||||||
|
cert, err := tls.X509KeyPair(localhostCert, localhostKey)
|
||||||
|
if err != nil {
|
||||||
|
panic(fmt.Sprintf("error creating key pair: %v", err))
|
||||||
|
}
|
||||||
|
serverTLSConfig.Certificates = []tls.Certificate{cert}
|
||||||
|
|
||||||
|
certificate, err := x509.ParseCertificate(serverTLSConfig.Certificates[0].Certificate[0])
|
||||||
|
if err != nil {
|
||||||
|
panic(fmt.Sprintf("error parsing x509 certificate: %v", err))
|
||||||
|
}
|
||||||
|
|
||||||
|
clientTLSConfig.RootCAs = x509.NewCertPool()
|
||||||
|
clientTLSConfig.RootCAs.AddCert(certificate)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestWithTimeout(t *testing.T) {
|
||||||
|
for _, recv := range []bool{true, false} {
|
||||||
|
for _, defaultTimout := range []time.Duration{0, time.Minute} {
|
||||||
|
var buf bytes.Buffer
|
||||||
|
nc := &testConn{Reader: strings.NewReader("+OK\r\n+OK\r\n+OK\r\n+OK\r\n+OK\r\n+OK\r\n+OK\r\n+OK\r\n+OK\r\n+OK\r\n"), Writer: &buf}
|
||||||
|
c, _ := redis.Dial("", "", redis.DialReadTimeout(defaultTimout), redis.DialNetDial(func(network, addr string) (net.Conn, error) { return nc, nil }))
|
||||||
|
for i := 0; i < 4; i++ {
|
||||||
|
var minDeadline, maxDeadline time.Time
|
||||||
|
|
||||||
|
// Alternate between default and specified timeout.
|
||||||
|
if i%2 == 0 {
|
||||||
|
if defaultTimout != 0 {
|
||||||
|
minDeadline = time.Now().Add(defaultTimout)
|
||||||
|
}
|
||||||
|
if recv {
|
||||||
|
c.Receive()
|
||||||
|
} else {
|
||||||
|
c.Do("PING")
|
||||||
|
}
|
||||||
|
if defaultTimout != 0 {
|
||||||
|
maxDeadline = time.Now().Add(defaultTimout)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
timeout := 10 * time.Minute
|
||||||
|
minDeadline = time.Now().Add(timeout)
|
||||||
|
if recv {
|
||||||
|
redis.ReceiveWithTimeout(c, timeout)
|
||||||
|
} else {
|
||||||
|
redis.DoWithTimeout(c, timeout, "PING")
|
||||||
|
}
|
||||||
|
maxDeadline = time.Now().Add(timeout)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Expect set deadline in expected range.
|
||||||
|
if nc.readDeadline.Before(minDeadline) || nc.readDeadline.After(maxDeadline) {
|
||||||
|
t.Errorf("recv %v, %d: do deadline error: %v, %v, %v", recv, i, minDeadline, nc.readDeadline, maxDeadline)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
4
vendor/github.com/garyburd/redigo/redis/doc.go
generated
vendored
4
vendor/github.com/garyburd/redigo/redis/doc.go
generated
vendored
@@ -38,7 +38,7 @@
|
|||||||
//
|
//
|
||||||
// n, err := conn.Do("APPEND", "key", "value")
|
// n, err := conn.Do("APPEND", "key", "value")
|
||||||
//
|
//
|
||||||
// The Do method converts command arguments to binary strings for transmission
|
// The Do method converts command arguments to bulk strings for transmission
|
||||||
// to the server as follows:
|
// to the server as follows:
|
||||||
//
|
//
|
||||||
// Go Type Conversion
|
// Go Type Conversion
|
||||||
@@ -48,7 +48,7 @@
|
|||||||
// float64 strconv.FormatFloat(v, 'g', -1, 64)
|
// float64 strconv.FormatFloat(v, 'g', -1, 64)
|
||||||
// bool true -> "1", false -> "0"
|
// bool true -> "1", false -> "0"
|
||||||
// nil ""
|
// nil ""
|
||||||
// all other types fmt.Print(v)
|
// all other types fmt.Fprint(w, v)
|
||||||
//
|
//
|
||||||
// Redis command reply types are represented using the following Go types:
|
// Redis command reply types are represented using the following Go types:
|
||||||
//
|
//
|
||||||
|
|||||||
@@ -4,11 +4,7 @@ package redis
|
|||||||
|
|
||||||
import "crypto/tls"
|
import "crypto/tls"
|
||||||
|
|
||||||
// similar cloneTLSClientConfig in the stdlib, but also honor skipVerify for the nil case
|
func cloneTLSConfig(cfg *tls.Config) *tls.Config {
|
||||||
func cloneTLSClientConfig(cfg *tls.Config, skipVerify bool) *tls.Config {
|
|
||||||
if cfg == nil {
|
|
||||||
return &tls.Config{InsecureSkipVerify: skipVerify}
|
|
||||||
}
|
|
||||||
return &tls.Config{
|
return &tls.Config{
|
||||||
Rand: cfg.Rand,
|
Rand: cfg.Rand,
|
||||||
Time: cfg.Time,
|
Time: cfg.Time,
|
||||||
8
vendor/github.com/garyburd/redigo/redis/go17.go
generated
vendored
8
vendor/github.com/garyburd/redigo/redis/go17.go
generated
vendored
@@ -1,14 +1,10 @@
|
|||||||
// +build go1.7
|
// +build go1.7,!go1.8
|
||||||
|
|
||||||
package redis
|
package redis
|
||||||
|
|
||||||
import "crypto/tls"
|
import "crypto/tls"
|
||||||
|
|
||||||
// similar cloneTLSClientConfig in the stdlib, but also honor skipVerify for the nil case
|
func cloneTLSConfig(cfg *tls.Config) *tls.Config {
|
||||||
func cloneTLSClientConfig(cfg *tls.Config, skipVerify bool) *tls.Config {
|
|
||||||
if cfg == nil {
|
|
||||||
return &tls.Config{InsecureSkipVerify: skipVerify}
|
|
||||||
}
|
|
||||||
return &tls.Config{
|
return &tls.Config{
|
||||||
Rand: cfg.Rand,
|
Rand: cfg.Rand,
|
||||||
Time: cfg.Time,
|
Time: cfg.Time,
|
||||||
|
|||||||
9
vendor/github.com/garyburd/redigo/redis/go18.go
generated
vendored
Normal file
9
vendor/github.com/garyburd/redigo/redis/go18.go
generated
vendored
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
// +build go1.8
|
||||||
|
|
||||||
|
package redis
|
||||||
|
|
||||||
|
import "crypto/tls"
|
||||||
|
|
||||||
|
func cloneTLSConfig(cfg *tls.Config) *tls.Config {
|
||||||
|
return cfg.Clone()
|
||||||
|
}
|
||||||
17
vendor/github.com/garyburd/redigo/redis/log.go
generated
vendored
17
vendor/github.com/garyburd/redigo/redis/log.go
generated
vendored
@@ -18,6 +18,11 @@ import (
|
|||||||
"bytes"
|
"bytes"
|
||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
_ ConnWithTimeout = (*loggingConn)(nil)
|
||||||
)
|
)
|
||||||
|
|
||||||
// NewLoggingConn returns a logging wrapper around a connection.
|
// NewLoggingConn returns a logging wrapper around a connection.
|
||||||
@@ -104,6 +109,12 @@ func (c *loggingConn) Do(commandName string, args ...interface{}) (interface{},
|
|||||||
return reply, err
|
return reply, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *loggingConn) DoWithTimeout(timeout time.Duration, commandName string, args ...interface{}) (interface{}, error) {
|
||||||
|
reply, err := DoWithTimeout(c.Conn, timeout, commandName, args...)
|
||||||
|
c.print("DoWithTimeout", commandName, args, reply, err)
|
||||||
|
return reply, err
|
||||||
|
}
|
||||||
|
|
||||||
func (c *loggingConn) Send(commandName string, args ...interface{}) error {
|
func (c *loggingConn) Send(commandName string, args ...interface{}) error {
|
||||||
err := c.Conn.Send(commandName, args...)
|
err := c.Conn.Send(commandName, args...)
|
||||||
c.print("Send", commandName, args, nil, err)
|
c.print("Send", commandName, args, nil, err)
|
||||||
@@ -115,3 +126,9 @@ func (c *loggingConn) Receive() (interface{}, error) {
|
|||||||
c.print("Receive", "", nil, reply, err)
|
c.print("Receive", "", nil, reply, err)
|
||||||
return reply, err
|
return reply, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *loggingConn) ReceiveWithTimeout(timeout time.Duration) (interface{}, error) {
|
||||||
|
reply, err := ReceiveWithTimeout(c.Conn, timeout)
|
||||||
|
c.print("ReceiveWithTimeout", "", nil, reply, err)
|
||||||
|
return reply, err
|
||||||
|
}
|
||||||
|
|||||||
61
vendor/github.com/garyburd/redigo/redis/pool.go
generated
vendored
61
vendor/github.com/garyburd/redigo/redis/pool.go
generated
vendored
@@ -28,6 +28,11 @@ import (
|
|||||||
"github.com/garyburd/redigo/internal"
|
"github.com/garyburd/redigo/internal"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
_ ConnWithTimeout = (*pooledConnection)(nil)
|
||||||
|
_ ConnWithTimeout = (*errorConnection)(nil)
|
||||||
|
)
|
||||||
|
|
||||||
var nowFunc = time.Now // for testing
|
var nowFunc = time.Now // for testing
|
||||||
|
|
||||||
// ErrPoolExhausted is returned from a pool connection method (Do, Send,
|
// ErrPoolExhausted is returned from a pool connection method (Do, Send,
|
||||||
@@ -96,7 +101,7 @@ var (
|
|||||||
// return nil, err
|
// return nil, err
|
||||||
// }
|
// }
|
||||||
// return c, nil
|
// return c, nil
|
||||||
// }
|
// },
|
||||||
// }
|
// }
|
||||||
//
|
//
|
||||||
// Use the TestOnBorrow function to check the health of an idle connection
|
// Use the TestOnBorrow function to check the health of an idle connection
|
||||||
@@ -115,7 +120,6 @@ var (
|
|||||||
// }
|
// }
|
||||||
//
|
//
|
||||||
type Pool struct {
|
type Pool struct {
|
||||||
|
|
||||||
// Dial is an application supplied function for creating and configuring a
|
// Dial is an application supplied function for creating and configuring a
|
||||||
// connection.
|
// connection.
|
||||||
//
|
//
|
||||||
@@ -181,6 +185,26 @@ func (p *Pool) Get() Conn {
|
|||||||
return &pooledConnection{p: p, c: c}
|
return &pooledConnection{p: p, c: c}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// PoolStats contains pool statistics.
|
||||||
|
type PoolStats struct {
|
||||||
|
// ActiveCount is the number of connections in the pool. The count includes idle connections and connections in use.
|
||||||
|
ActiveCount int
|
||||||
|
// IdleCount is the number of idle connections in the pool.
|
||||||
|
IdleCount int
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stats returns pool's statistics.
|
||||||
|
func (p *Pool) Stats() PoolStats {
|
||||||
|
p.mu.Lock()
|
||||||
|
stats := PoolStats{
|
||||||
|
ActiveCount: p.active,
|
||||||
|
IdleCount: p.idle.Len(),
|
||||||
|
}
|
||||||
|
p.mu.Unlock()
|
||||||
|
|
||||||
|
return stats
|
||||||
|
}
|
||||||
|
|
||||||
// ActiveCount returns the number of connections in the pool. The count includes idle connections and connections in use.
|
// ActiveCount returns the number of connections in the pool. The count includes idle connections and connections in use.
|
||||||
func (p *Pool) ActiveCount() int {
|
func (p *Pool) ActiveCount() int {
|
||||||
p.mu.Lock()
|
p.mu.Lock()
|
||||||
@@ -249,7 +273,6 @@ func (p *Pool) get() (Conn, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for {
|
for {
|
||||||
|
|
||||||
// Get idle connection.
|
// Get idle connection.
|
||||||
|
|
||||||
for i, n := 0, p.idle.Len(); i < n; i++ {
|
for i, n := 0, p.idle.Len(); i < n; i++ {
|
||||||
@@ -400,6 +423,16 @@ func (pc *pooledConnection) Do(commandName string, args ...interface{}) (reply i
|
|||||||
return pc.c.Do(commandName, args...)
|
return pc.c.Do(commandName, args...)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (pc *pooledConnection) DoWithTimeout(timeout time.Duration, commandName string, args ...interface{}) (reply interface{}, err error) {
|
||||||
|
cwt, ok := pc.c.(ConnWithTimeout)
|
||||||
|
if !ok {
|
||||||
|
return nil, errTimeoutNotSupported
|
||||||
|
}
|
||||||
|
ci := internal.LookupCommandInfo(commandName)
|
||||||
|
pc.state = (pc.state | ci.Set) &^ ci.Clear
|
||||||
|
return cwt.DoWithTimeout(timeout, commandName, args...)
|
||||||
|
}
|
||||||
|
|
||||||
func (pc *pooledConnection) Send(commandName string, args ...interface{}) error {
|
func (pc *pooledConnection) Send(commandName string, args ...interface{}) error {
|
||||||
ci := internal.LookupCommandInfo(commandName)
|
ci := internal.LookupCommandInfo(commandName)
|
||||||
pc.state = (pc.state | ci.Set) &^ ci.Clear
|
pc.state = (pc.state | ci.Set) &^ ci.Clear
|
||||||
@@ -414,11 +447,23 @@ func (pc *pooledConnection) Receive() (reply interface{}, err error) {
|
|||||||
return pc.c.Receive()
|
return pc.c.Receive()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (pc *pooledConnection) ReceiveWithTimeout(timeout time.Duration) (reply interface{}, err error) {
|
||||||
|
cwt, ok := pc.c.(ConnWithTimeout)
|
||||||
|
if !ok {
|
||||||
|
return nil, errTimeoutNotSupported
|
||||||
|
}
|
||||||
|
return cwt.ReceiveWithTimeout(timeout)
|
||||||
|
}
|
||||||
|
|
||||||
type errorConnection struct{ err error }
|
type errorConnection struct{ err error }
|
||||||
|
|
||||||
func (ec errorConnection) Do(string, ...interface{}) (interface{}, error) { return nil, ec.err }
|
func (ec errorConnection) Do(string, ...interface{}) (interface{}, error) { return nil, ec.err }
|
||||||
func (ec errorConnection) Send(string, ...interface{}) error { return ec.err }
|
func (ec errorConnection) DoWithTimeout(time.Duration, string, ...interface{}) (interface{}, error) {
|
||||||
func (ec errorConnection) Err() error { return ec.err }
|
return nil, ec.err
|
||||||
func (ec errorConnection) Close() error { return ec.err }
|
}
|
||||||
func (ec errorConnection) Flush() error { return ec.err }
|
func (ec errorConnection) Send(string, ...interface{}) error { return ec.err }
|
||||||
func (ec errorConnection) Receive() (interface{}, error) { return nil, ec.err }
|
func (ec errorConnection) Err() error { return ec.err }
|
||||||
|
func (ec errorConnection) Close() error { return nil }
|
||||||
|
func (ec errorConnection) Flush() error { return ec.err }
|
||||||
|
func (ec errorConnection) Receive() (interface{}, error) { return nil, ec.err }
|
||||||
|
func (ec errorConnection) ReceiveWithTimeout(time.Duration) (interface{}, error) { return nil, ec.err }
|
||||||
|
|||||||
11
vendor/github.com/garyburd/redigo/redis/pool_test.go
generated
vendored
11
vendor/github.com/garyburd/redigo/redis/pool_test.go
generated
vendored
@@ -92,12 +92,15 @@ func (d *poolDialer) check(message string, p *redis.Pool, dialed, open, inuse in
|
|||||||
d.t.Errorf("%s: open=%d, want %d", message, d.open, open)
|
d.t.Errorf("%s: open=%d, want %d", message, d.open, open)
|
||||||
}
|
}
|
||||||
|
|
||||||
if active := p.ActiveCount(); active != open {
|
stats := p.Stats()
|
||||||
d.t.Errorf("%s: active=%d, want %d", message, active, open)
|
|
||||||
|
if stats.ActiveCount != open {
|
||||||
|
d.t.Errorf("%s: active=%d, want %d", message, stats.ActiveCount, open)
|
||||||
}
|
}
|
||||||
if idle := p.IdleCount(); idle != open-inuse {
|
if stats.IdleCount != open-inuse {
|
||||||
d.t.Errorf("%s: idle=%d, want %d", message, idle, open-inuse)
|
d.t.Errorf("%s: idle=%d, want %d", message, stats.IdleCount, open-inuse)
|
||||||
}
|
}
|
||||||
|
|
||||||
d.mu.Unlock()
|
d.mu.Unlock()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
23
vendor/github.com/garyburd/redigo/redis/pubsub.go
generated
vendored
23
vendor/github.com/garyburd/redigo/redis/pubsub.go
generated
vendored
@@ -14,11 +14,13 @@
|
|||||||
|
|
||||||
package redis
|
package redis
|
||||||
|
|
||||||
import "errors"
|
import (
|
||||||
|
"errors"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
// Subscription represents a subscribe or unsubscribe notification.
|
// Subscription represents a subscribe or unsubscribe notification.
|
||||||
type Subscription struct {
|
type Subscription struct {
|
||||||
|
|
||||||
// Kind is "subscribe", "unsubscribe", "psubscribe" or "punsubscribe"
|
// Kind is "subscribe", "unsubscribe", "psubscribe" or "punsubscribe"
|
||||||
Kind string
|
Kind string
|
||||||
|
|
||||||
@@ -31,7 +33,6 @@ type Subscription struct {
|
|||||||
|
|
||||||
// Message represents a message notification.
|
// Message represents a message notification.
|
||||||
type Message struct {
|
type Message struct {
|
||||||
|
|
||||||
// The originating channel.
|
// The originating channel.
|
||||||
Channel string
|
Channel string
|
||||||
|
|
||||||
@@ -41,7 +42,6 @@ type Message struct {
|
|||||||
|
|
||||||
// PMessage represents a pmessage notification.
|
// PMessage represents a pmessage notification.
|
||||||
type PMessage struct {
|
type PMessage struct {
|
||||||
|
|
||||||
// The matched pattern.
|
// The matched pattern.
|
||||||
Pattern string
|
Pattern string
|
||||||
|
|
||||||
@@ -94,6 +94,9 @@ func (c PubSubConn) PUnsubscribe(channel ...interface{}) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Ping sends a PING to the server with the specified data.
|
// Ping sends a PING to the server with the specified data.
|
||||||
|
//
|
||||||
|
// The connection must be subscribed to at least one channel or pattern when
|
||||||
|
// calling this method.
|
||||||
func (c PubSubConn) Ping(data string) error {
|
func (c PubSubConn) Ping(data string) error {
|
||||||
c.Conn.Send("PING", data)
|
c.Conn.Send("PING", data)
|
||||||
return c.Conn.Flush()
|
return c.Conn.Flush()
|
||||||
@@ -103,7 +106,17 @@ func (c PubSubConn) Ping(data string) error {
|
|||||||
// or error. The return value is intended to be used directly in a type switch
|
// or error. The return value is intended to be used directly in a type switch
|
||||||
// as illustrated in the PubSubConn example.
|
// as illustrated in the PubSubConn example.
|
||||||
func (c PubSubConn) Receive() interface{} {
|
func (c PubSubConn) Receive() interface{} {
|
||||||
reply, err := Values(c.Conn.Receive())
|
return c.receiveInternal(c.Conn.Receive())
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReceiveWithTimeout is like Receive, but it allows the application to
|
||||||
|
// override the connection's default timeout.
|
||||||
|
func (c PubSubConn) ReceiveWithTimeout(timeout time.Duration) interface{} {
|
||||||
|
return c.receiveInternal(ReceiveWithTimeout(c.Conn, timeout))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c PubSubConn) receiveInternal(replyArg interface{}, errArg error) interface{} {
|
||||||
|
reply, err := Values(replyArg, errArg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|||||||
165
vendor/github.com/garyburd/redigo/redis/pubsub_example_test.go
generated
vendored
Normal file
165
vendor/github.com/garyburd/redigo/redis/pubsub_example_test.go
generated
vendored
Normal file
@@ -0,0 +1,165 @@
|
|||||||
|
// Copyright 2012 Gary Burd
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License"): you may
|
||||||
|
// not use this file except in compliance with the License. You may obtain
|
||||||
|
// a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
// License for the specific language governing permissions and limitations
|
||||||
|
// under the License.
|
||||||
|
|
||||||
|
// +build go1.7
|
||||||
|
|
||||||
|
package redis_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/garyburd/redigo/redis"
|
||||||
|
)
|
||||||
|
|
||||||
|
// listenPubSubChannels listens for messages on Redis pubsub channels. The
|
||||||
|
// onStart function is called after the channels are subscribed. The onMessage
|
||||||
|
// function is called for each message.
|
||||||
|
func listenPubSubChannels(ctx context.Context, redisServerAddr string,
|
||||||
|
onStart func() error,
|
||||||
|
onMessage func(channel string, data []byte) error,
|
||||||
|
channels ...string) error {
|
||||||
|
// A ping is set to the server with this period to test for the health of
|
||||||
|
// the connection and server.
|
||||||
|
const healthCheckPeriod = time.Minute
|
||||||
|
|
||||||
|
c, err := redis.Dial("tcp", redisServerAddr,
|
||||||
|
// Read timeout on server should be greater than ping period.
|
||||||
|
redis.DialReadTimeout(healthCheckPeriod+10*time.Second),
|
||||||
|
redis.DialWriteTimeout(10*time.Second))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer c.Close()
|
||||||
|
|
||||||
|
psc := redis.PubSubConn{Conn: c}
|
||||||
|
|
||||||
|
if err := psc.Subscribe(redis.Args{}.AddFlat(channels)...); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
done := make(chan error, 1)
|
||||||
|
|
||||||
|
// Start a goroutine to receive notifications from the server.
|
||||||
|
go func() {
|
||||||
|
for {
|
||||||
|
switch n := psc.Receive().(type) {
|
||||||
|
case error:
|
||||||
|
done <- n
|
||||||
|
return
|
||||||
|
case redis.Message:
|
||||||
|
if err := onMessage(n.Channel, n.Data); err != nil {
|
||||||
|
done <- err
|
||||||
|
return
|
||||||
|
}
|
||||||
|
case redis.Subscription:
|
||||||
|
switch n.Count {
|
||||||
|
case len(channels):
|
||||||
|
// Notify application when all channels are subscribed.
|
||||||
|
if err := onStart(); err != nil {
|
||||||
|
done <- err
|
||||||
|
return
|
||||||
|
}
|
||||||
|
case 0:
|
||||||
|
// Return from the goroutine when all channels are unsubscribed.
|
||||||
|
done <- nil
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
ticker := time.NewTicker(healthCheckPeriod)
|
||||||
|
defer ticker.Stop()
|
||||||
|
loop:
|
||||||
|
for err == nil {
|
||||||
|
select {
|
||||||
|
case <-ticker.C:
|
||||||
|
// Send ping to test health of connection and server. If
|
||||||
|
// corresponding pong is not received, then receive on the
|
||||||
|
// connection will timeout and the receive goroutine will exit.
|
||||||
|
if err = psc.Ping(""); err != nil {
|
||||||
|
break loop
|
||||||
|
}
|
||||||
|
case <-ctx.Done():
|
||||||
|
break loop
|
||||||
|
case err := <-done:
|
||||||
|
// Return error from the receive goroutine.
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Signal the receiving goroutine to exit by unsubscribing from all channels.
|
||||||
|
psc.Unsubscribe()
|
||||||
|
|
||||||
|
// Wait for goroutine to complete.
|
||||||
|
return <-done
|
||||||
|
}
|
||||||
|
|
||||||
|
func publish() {
|
||||||
|
c, err := dial()
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer c.Close()
|
||||||
|
|
||||||
|
c.Do("PUBLISH", "c1", "hello")
|
||||||
|
c.Do("PUBLISH", "c2", "world")
|
||||||
|
c.Do("PUBLISH", "c1", "goodbye")
|
||||||
|
}
|
||||||
|
|
||||||
|
// This example shows how receive pubsub notifications with cancelation and
|
||||||
|
// health checks.
|
||||||
|
func ExamplePubSubConn() {
|
||||||
|
redisServerAddr, err := serverAddr()
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
|
|
||||||
|
err = listenPubSubChannels(ctx,
|
||||||
|
redisServerAddr,
|
||||||
|
func() error {
|
||||||
|
// The start callback is a good place to backfill missed
|
||||||
|
// notifications. For the purpose of this example, a goroutine is
|
||||||
|
// started to send notifications.
|
||||||
|
go publish()
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
func(channel string, message []byte) error {
|
||||||
|
fmt.Printf("channel: %s, message: %s\n", channel, message)
|
||||||
|
|
||||||
|
// For the purpose of this example, cancel the listener's context
|
||||||
|
// after receiving last message sent by publish().
|
||||||
|
if string(message) == "goodbye" {
|
||||||
|
cancel()
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
"c1", "c2")
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
// channel: c1, message: hello
|
||||||
|
// channel: c2, message: world
|
||||||
|
// channel: c1, message: goodbye
|
||||||
|
}
|
||||||
88
vendor/github.com/garyburd/redigo/redis/pubsub_test.go
generated
vendored
88
vendor/github.com/garyburd/redigo/redis/pubsub_test.go
generated
vendored
@@ -15,93 +15,13 @@
|
|||||||
package redis_test
|
package redis_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
"reflect"
|
"reflect"
|
||||||
"sync"
|
|
||||||
"testing"
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/garyburd/redigo/redis"
|
"github.com/garyburd/redigo/redis"
|
||||||
)
|
)
|
||||||
|
|
||||||
func publish(channel, value interface{}) {
|
|
||||||
c, err := dial()
|
|
||||||
if err != nil {
|
|
||||||
fmt.Println(err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
defer c.Close()
|
|
||||||
c.Do("PUBLISH", channel, value)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Applications can receive pushed messages from one goroutine and manage subscriptions from another goroutine.
|
|
||||||
func ExamplePubSubConn() {
|
|
||||||
c, err := dial()
|
|
||||||
if err != nil {
|
|
||||||
fmt.Println(err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
defer c.Close()
|
|
||||||
var wg sync.WaitGroup
|
|
||||||
wg.Add(2)
|
|
||||||
|
|
||||||
psc := redis.PubSubConn{Conn: c}
|
|
||||||
|
|
||||||
// This goroutine receives and prints pushed notifications from the server.
|
|
||||||
// The goroutine exits when the connection is unsubscribed from all
|
|
||||||
// channels or there is an error.
|
|
||||||
go func() {
|
|
||||||
defer wg.Done()
|
|
||||||
for {
|
|
||||||
switch n := psc.Receive().(type) {
|
|
||||||
case redis.Message:
|
|
||||||
fmt.Printf("Message: %s %s\n", n.Channel, n.Data)
|
|
||||||
case redis.PMessage:
|
|
||||||
fmt.Printf("PMessage: %s %s %s\n", n.Pattern, n.Channel, n.Data)
|
|
||||||
case redis.Subscription:
|
|
||||||
fmt.Printf("Subscription: %s %s %d\n", n.Kind, n.Channel, n.Count)
|
|
||||||
if n.Count == 0 {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
case error:
|
|
||||||
fmt.Printf("error: %v\n", n)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
// This goroutine manages subscriptions for the connection.
|
|
||||||
go func() {
|
|
||||||
defer wg.Done()
|
|
||||||
|
|
||||||
psc.Subscribe("example")
|
|
||||||
psc.PSubscribe("p*")
|
|
||||||
|
|
||||||
// The following function calls publish a message using another
|
|
||||||
// connection to the Redis server.
|
|
||||||
publish("example", "hello")
|
|
||||||
publish("example", "world")
|
|
||||||
publish("pexample", "foo")
|
|
||||||
publish("pexample", "bar")
|
|
||||||
|
|
||||||
// Unsubscribe from all connections. This will cause the receiving
|
|
||||||
// goroutine to exit.
|
|
||||||
psc.Unsubscribe()
|
|
||||||
psc.PUnsubscribe()
|
|
||||||
}()
|
|
||||||
|
|
||||||
wg.Wait()
|
|
||||||
|
|
||||||
// Output:
|
|
||||||
// Subscription: subscribe example 1
|
|
||||||
// Subscription: psubscribe p* 2
|
|
||||||
// Message: example hello
|
|
||||||
// Message: example world
|
|
||||||
// PMessage: p* pexample foo
|
|
||||||
// PMessage: p* pexample bar
|
|
||||||
// Subscription: unsubscribe example 1
|
|
||||||
// Subscription: punsubscribe p* 0
|
|
||||||
}
|
|
||||||
|
|
||||||
func expectPushed(t *testing.T, c redis.PubSubConn, message string, expected interface{}) {
|
func expectPushed(t *testing.T, c redis.PubSubConn, message string, expected interface{}) {
|
||||||
actual := c.Receive()
|
actual := c.Receive()
|
||||||
if !reflect.DeepEqual(actual, expected) {
|
if !reflect.DeepEqual(actual, expected) {
|
||||||
@@ -145,4 +65,10 @@ func TestPushed(t *testing.T) {
|
|||||||
c.Conn.Send("PING")
|
c.Conn.Send("PING")
|
||||||
c.Conn.Flush()
|
c.Conn.Flush()
|
||||||
expectPushed(t, c, `Send("PING")`, redis.Pong{})
|
expectPushed(t, c, `Send("PING")`, redis.Pong{})
|
||||||
|
|
||||||
|
c.Ping("timeout")
|
||||||
|
got := c.ReceiveWithTimeout(time.Minute)
|
||||||
|
if want := (redis.Pong{Data: "timeout"}); want != got {
|
||||||
|
t.Errorf("recv /w timeout got %v, want %v", got, want)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
72
vendor/github.com/garyburd/redigo/redis/redis.go
generated
vendored
72
vendor/github.com/garyburd/redigo/redis/redis.go
generated
vendored
@@ -14,6 +14,11 @@
|
|||||||
|
|
||||||
package redis
|
package redis
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
// Error represents an error returned in a command reply.
|
// Error represents an error returned in a command reply.
|
||||||
type Error string
|
type Error string
|
||||||
|
|
||||||
@@ -40,20 +45,73 @@ type Conn interface {
|
|||||||
Receive() (reply interface{}, err error)
|
Receive() (reply interface{}, err error)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Argument is implemented by types which want to control how their value is
|
// Argument is the interface implemented by an object which wants to control how
|
||||||
// interpreted when used as an argument to a redis command.
|
// the object is converted to Redis bulk strings.
|
||||||
type Argument interface {
|
type Argument interface {
|
||||||
// RedisArg returns the interface that represents the value to be used
|
// RedisArg returns a value to be encoded as a bulk string per the
|
||||||
// in redis commands.
|
// conversions listed in the section 'Executing Commands'.
|
||||||
|
// Implementations should typically return a []byte or string.
|
||||||
RedisArg() interface{}
|
RedisArg() interface{}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Scanner is implemented by types which want to control how their value is
|
// Scanner is implemented by an object which wants to control its value is
|
||||||
// interpreted when read from redis.
|
// interpreted when read from Redis.
|
||||||
type Scanner interface {
|
type Scanner interface {
|
||||||
// RedisScan assigns a value from a redis value.
|
// RedisScan assigns a value from a Redis value. The argument src is one of
|
||||||
|
// the reply types listed in the section `Executing Commands`.
|
||||||
//
|
//
|
||||||
// An error should be returned if the value cannot be stored without
|
// An error should be returned if the value cannot be stored without
|
||||||
// loss of information.
|
// loss of information.
|
||||||
RedisScan(src interface{}) error
|
RedisScan(src interface{}) error
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ConnWithTimeout is an optional interface that allows the caller to override
|
||||||
|
// a connection's default read timeout. This interface is useful for executing
|
||||||
|
// the BLPOP, BRPOP, BRPOPLPUSH, XREAD and other commands that block at the
|
||||||
|
// server.
|
||||||
|
//
|
||||||
|
// A connection's default read timeout is set with the DialReadTimeout dial
|
||||||
|
// option. Applications should rely on the default timeout for commands that do
|
||||||
|
// not block at the server.
|
||||||
|
//
|
||||||
|
// All of the Conn implementations in this package satisfy the ConnWithTimeout
|
||||||
|
// interface.
|
||||||
|
//
|
||||||
|
// Use the DoWithTimeout and ReceiveWithTimeout helper functions to simplify
|
||||||
|
// use of this interface.
|
||||||
|
type ConnWithTimeout interface {
|
||||||
|
Conn
|
||||||
|
|
||||||
|
// Do sends a command to the server and returns the received reply.
|
||||||
|
// The timeout overrides the read timeout set when dialing the
|
||||||
|
// connection.
|
||||||
|
DoWithTimeout(timeout time.Duration, commandName string, args ...interface{}) (reply interface{}, err error)
|
||||||
|
|
||||||
|
// Receive receives a single reply from the Redis server. The timeout
|
||||||
|
// overrides the read timeout set when dialing the connection.
|
||||||
|
ReceiveWithTimeout(timeout time.Duration) (reply interface{}, err error)
|
||||||
|
}
|
||||||
|
|
||||||
|
var errTimeoutNotSupported = errors.New("redis: connection does not support ConnWithTimeout")
|
||||||
|
|
||||||
|
// DoWithTimeout executes a Redis command with the specified read timeout. If
|
||||||
|
// the connection does not satisfy the ConnWithTimeout interface, then an error
|
||||||
|
// is returned.
|
||||||
|
func DoWithTimeout(c Conn, timeout time.Duration, cmd string, args ...interface{}) (interface{}, error) {
|
||||||
|
cwt, ok := c.(ConnWithTimeout)
|
||||||
|
if !ok {
|
||||||
|
return nil, errTimeoutNotSupported
|
||||||
|
}
|
||||||
|
return cwt.DoWithTimeout(timeout, cmd, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReceiveWithTimeout receives a reply with the specified read timeout. If the
|
||||||
|
// connection does not satisfy the ConnWithTimeout interface, then an error is
|
||||||
|
// returned.
|
||||||
|
func ReceiveWithTimeout(c Conn, timeout time.Duration) (interface{}, error) {
|
||||||
|
cwt, ok := c.(ConnWithTimeout)
|
||||||
|
if !ok {
|
||||||
|
return nil, errTimeoutNotSupported
|
||||||
|
}
|
||||||
|
return cwt.ReceiveWithTimeout(timeout)
|
||||||
|
}
|
||||||
|
|||||||
71
vendor/github.com/garyburd/redigo/redis/redis_test.go
generated
vendored
Normal file
71
vendor/github.com/garyburd/redigo/redis/redis_test.go
generated
vendored
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
// Copyright 2017 Gary Burd
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License"): you may
|
||||||
|
// not use this file except in compliance with the License. You may obtain
|
||||||
|
// a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
// License for the specific language governing permissions and limitations
|
||||||
|
// under the License.
|
||||||
|
|
||||||
|
package redis_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/garyburd/redigo/redis"
|
||||||
|
)
|
||||||
|
|
||||||
|
type timeoutTestConn int
|
||||||
|
|
||||||
|
func (tc timeoutTestConn) Do(string, ...interface{}) (interface{}, error) {
|
||||||
|
return time.Duration(-1), nil
|
||||||
|
}
|
||||||
|
func (tc timeoutTestConn) DoWithTimeout(timeout time.Duration, cmd string, args ...interface{}) (interface{}, error) {
|
||||||
|
return timeout, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tc timeoutTestConn) Receive() (interface{}, error) {
|
||||||
|
return time.Duration(-1), nil
|
||||||
|
}
|
||||||
|
func (tc timeoutTestConn) ReceiveWithTimeout(timeout time.Duration) (interface{}, error) {
|
||||||
|
return timeout, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tc timeoutTestConn) Send(string, ...interface{}) error { return nil }
|
||||||
|
func (tc timeoutTestConn) Err() error { return nil }
|
||||||
|
func (tc timeoutTestConn) Close() error { return nil }
|
||||||
|
func (tc timeoutTestConn) Flush() error { return nil }
|
||||||
|
|
||||||
|
func testTimeout(t *testing.T, c redis.Conn) {
|
||||||
|
r, err := c.Do("PING")
|
||||||
|
if r != time.Duration(-1) || err != nil {
|
||||||
|
t.Errorf("Do() = %v, %v, want %v, %v", r, err, time.Duration(-1), nil)
|
||||||
|
}
|
||||||
|
r, err = redis.DoWithTimeout(c, time.Minute, "PING")
|
||||||
|
if r != time.Minute || err != nil {
|
||||||
|
t.Errorf("DoWithTimeout() = %v, %v, want %v, %v", r, err, time.Minute, nil)
|
||||||
|
}
|
||||||
|
r, err = c.Receive()
|
||||||
|
if r != time.Duration(-1) || err != nil {
|
||||||
|
t.Errorf("Receive() = %v, %v, want %v, %v", r, err, time.Duration(-1), nil)
|
||||||
|
}
|
||||||
|
r, err = redis.ReceiveWithTimeout(c, time.Minute)
|
||||||
|
if r != time.Minute || err != nil {
|
||||||
|
t.Errorf("ReceiveWithTimeout() = %v, %v, want %v, %v", r, err, time.Minute, nil)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestConnTimeout(t *testing.T) {
|
||||||
|
testTimeout(t, timeoutTestConn(0))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPoolConnTimeout(t *testing.T) {
|
||||||
|
p := &redis.Pool{Dial: func() (redis.Conn, error) { return timeoutTestConn(0), nil }}
|
||||||
|
testTimeout(t, p.Get())
|
||||||
|
}
|
||||||
164
vendor/github.com/garyburd/redigo/redis/reply.go
generated
vendored
164
vendor/github.com/garyburd/redigo/redis/reply.go
generated
vendored
@@ -243,34 +243,67 @@ func Values(reply interface{}, err error) ([]interface{}, error) {
|
|||||||
return nil, fmt.Errorf("redigo: unexpected type for Values, got type %T", reply)
|
return nil, fmt.Errorf("redigo: unexpected type for Values, got type %T", reply)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func sliceHelper(reply interface{}, err error, name string, makeSlice func(int), assign func(int, interface{}) error) error {
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
switch reply := reply.(type) {
|
||||||
|
case []interface{}:
|
||||||
|
makeSlice(len(reply))
|
||||||
|
for i := range reply {
|
||||||
|
if reply[i] == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if err := assign(i, reply[i]); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
case nil:
|
||||||
|
return ErrNil
|
||||||
|
case Error:
|
||||||
|
return reply
|
||||||
|
}
|
||||||
|
return fmt.Errorf("redigo: unexpected type for %s, got type %T", name, reply)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Float64s is a helper that converts an array command reply to a []float64. If
|
||||||
|
// err is not equal to nil, then Float64s returns nil, err. Nil array items are
|
||||||
|
// converted to 0 in the output slice. Floats64 returns an error if an array
|
||||||
|
// item is not a bulk string or nil.
|
||||||
|
func Float64s(reply interface{}, err error) ([]float64, error) {
|
||||||
|
var result []float64
|
||||||
|
err = sliceHelper(reply, err, "Float64s", func(n int) { result = make([]float64, n) }, func(i int, v interface{}) error {
|
||||||
|
p, ok := v.([]byte)
|
||||||
|
if !ok {
|
||||||
|
return fmt.Errorf("redigo: unexpected element type for Floats64, got type %T", v)
|
||||||
|
}
|
||||||
|
f, err := strconv.ParseFloat(string(p), 64)
|
||||||
|
result[i] = f
|
||||||
|
return err
|
||||||
|
})
|
||||||
|
return result, err
|
||||||
|
}
|
||||||
|
|
||||||
// Strings is a helper that converts an array command reply to a []string. If
|
// Strings is a helper that converts an array command reply to a []string. If
|
||||||
// err is not equal to nil, then Strings returns nil, err. Nil array items are
|
// err is not equal to nil, then Strings returns nil, err. Nil array items are
|
||||||
// converted to "" in the output slice. Strings returns an error if an array
|
// converted to "" in the output slice. Strings returns an error if an array
|
||||||
// item is not a bulk string or nil.
|
// item is not a bulk string or nil.
|
||||||
func Strings(reply interface{}, err error) ([]string, error) {
|
func Strings(reply interface{}, err error) ([]string, error) {
|
||||||
if err != nil {
|
var result []string
|
||||||
return nil, err
|
err = sliceHelper(reply, err, "Strings", func(n int) { result = make([]string, n) }, func(i int, v interface{}) error {
|
||||||
}
|
switch v := v.(type) {
|
||||||
switch reply := reply.(type) {
|
case string:
|
||||||
case []interface{}:
|
result[i] = v
|
||||||
result := make([]string, len(reply))
|
return nil
|
||||||
for i := range reply {
|
case []byte:
|
||||||
if reply[i] == nil {
|
result[i] = string(v)
|
||||||
continue
|
return nil
|
||||||
}
|
default:
|
||||||
p, ok := reply[i].([]byte)
|
return fmt.Errorf("redigo: unexpected element type for Strings, got type %T", v)
|
||||||
if !ok {
|
|
||||||
return nil, fmt.Errorf("redigo: unexpected element type for Strings, got type %T", reply[i])
|
|
||||||
}
|
|
||||||
result[i] = string(p)
|
|
||||||
}
|
}
|
||||||
return result, nil
|
})
|
||||||
case nil:
|
return result, err
|
||||||
return nil, ErrNil
|
|
||||||
case Error:
|
|
||||||
return nil, reply
|
|
||||||
}
|
|
||||||
return nil, fmt.Errorf("redigo: unexpected type for Strings, got type %T", reply)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ByteSlices is a helper that converts an array command reply to a [][]byte.
|
// ByteSlices is a helper that converts an array command reply to a [][]byte.
|
||||||
@@ -278,43 +311,64 @@ func Strings(reply interface{}, err error) ([]string, error) {
|
|||||||
// items are stay nil. ByteSlices returns an error if an array item is not a
|
// items are stay nil. ByteSlices returns an error if an array item is not a
|
||||||
// bulk string or nil.
|
// bulk string or nil.
|
||||||
func ByteSlices(reply interface{}, err error) ([][]byte, error) {
|
func ByteSlices(reply interface{}, err error) ([][]byte, error) {
|
||||||
if err != nil {
|
var result [][]byte
|
||||||
return nil, err
|
err = sliceHelper(reply, err, "ByteSlices", func(n int) { result = make([][]byte, n) }, func(i int, v interface{}) error {
|
||||||
}
|
p, ok := v.([]byte)
|
||||||
switch reply := reply.(type) {
|
if !ok {
|
||||||
case []interface{}:
|
return fmt.Errorf("redigo: unexpected element type for ByteSlices, got type %T", v)
|
||||||
result := make([][]byte, len(reply))
|
|
||||||
for i := range reply {
|
|
||||||
if reply[i] == nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
p, ok := reply[i].([]byte)
|
|
||||||
if !ok {
|
|
||||||
return nil, fmt.Errorf("redigo: unexpected element type for ByteSlices, got type %T", reply[i])
|
|
||||||
}
|
|
||||||
result[i] = p
|
|
||||||
}
|
}
|
||||||
return result, nil
|
result[i] = p
|
||||||
case nil:
|
return nil
|
||||||
return nil, ErrNil
|
})
|
||||||
case Error:
|
return result, err
|
||||||
return nil, reply
|
|
||||||
}
|
|
||||||
return nil, fmt.Errorf("redigo: unexpected type for ByteSlices, got type %T", reply)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ints is a helper that converts an array command reply to a []int. If
|
// Int64s is a helper that converts an array command reply to a []int64.
|
||||||
// err is not equal to nil, then Ints returns nil, err.
|
// If err is not equal to nil, then Int64s returns nil, err. Nil array
|
||||||
|
// items are stay nil. Int64s returns an error if an array item is not a
|
||||||
|
// bulk string or nil.
|
||||||
|
func Int64s(reply interface{}, err error) ([]int64, error) {
|
||||||
|
var result []int64
|
||||||
|
err = sliceHelper(reply, err, "Int64s", func(n int) { result = make([]int64, n) }, func(i int, v interface{}) error {
|
||||||
|
switch v := v.(type) {
|
||||||
|
case int64:
|
||||||
|
result[i] = v
|
||||||
|
return nil
|
||||||
|
case []byte:
|
||||||
|
n, err := strconv.ParseInt(string(v), 10, 64)
|
||||||
|
result[i] = n
|
||||||
|
return err
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("redigo: unexpected element type for Int64s, got type %T", v)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return result, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ints is a helper that converts an array command reply to a []in.
|
||||||
|
// If err is not equal to nil, then Ints returns nil, err. Nil array
|
||||||
|
// items are stay nil. Ints returns an error if an array item is not a
|
||||||
|
// bulk string or nil.
|
||||||
func Ints(reply interface{}, err error) ([]int, error) {
|
func Ints(reply interface{}, err error) ([]int, error) {
|
||||||
var ints []int
|
var result []int
|
||||||
values, err := Values(reply, err)
|
err = sliceHelper(reply, err, "Ints", func(n int) { result = make([]int, n) }, func(i int, v interface{}) error {
|
||||||
if err != nil {
|
switch v := v.(type) {
|
||||||
return ints, err
|
case int64:
|
||||||
}
|
n := int(v)
|
||||||
if err := ScanSlice(values, &ints); err != nil {
|
if int64(n) != v {
|
||||||
return ints, err
|
return strconv.ErrRange
|
||||||
}
|
}
|
||||||
return ints, nil
|
result[i] = n
|
||||||
|
return nil
|
||||||
|
case []byte:
|
||||||
|
n, err := strconv.Atoi(string(v))
|
||||||
|
result[i] = n
|
||||||
|
return err
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("redigo: unexpected element type for Ints, got type %T", v)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return result, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// StringMap is a helper that converts an array of strings (alternating key, value)
|
// StringMap is a helper that converts an array of strings (alternating key, value)
|
||||||
|
|||||||
41
vendor/github.com/garyburd/redigo/redis/reply_test.go
generated
vendored
41
vendor/github.com/garyburd/redigo/redis/reply_test.go
generated
vendored
@@ -37,24 +37,44 @@ var replyTests = []struct {
|
|||||||
expected valueError
|
expected valueError
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
"ints([v1, v2])",
|
"ints([[]byte, []byte])",
|
||||||
ve(redis.Ints([]interface{}{[]byte("4"), []byte("5")}, nil)),
|
ve(redis.Ints([]interface{}{[]byte("4"), []byte("5")}, nil)),
|
||||||
ve([]int{4, 5}, nil),
|
ve([]int{4, 5}, nil),
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"ints([nt64, int64])",
|
||||||
|
ve(redis.Ints([]interface{}{int64(4), int64(5)}, nil)),
|
||||||
|
ve([]int{4, 5}, nil),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ints([[]byte, nil, []byte])",
|
||||||
|
ve(redis.Ints([]interface{}{[]byte("4"), nil, []byte("5")}, nil)),
|
||||||
|
ve([]int{4, 0, 5}, nil),
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"ints(nil)",
|
"ints(nil)",
|
||||||
ve(redis.Ints(nil, nil)),
|
ve(redis.Ints(nil, nil)),
|
||||||
ve([]int(nil), redis.ErrNil),
|
ve([]int(nil), redis.ErrNil),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"strings([v1, v2])",
|
"int64s([[]byte, []byte])",
|
||||||
|
ve(redis.Int64s([]interface{}{[]byte("4"), []byte("5")}, nil)),
|
||||||
|
ve([]int64{4, 5}, nil),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"int64s([int64, int64])",
|
||||||
|
ve(redis.Int64s([]interface{}{int64(4), int64(5)}, nil)),
|
||||||
|
ve([]int64{4, 5}, nil),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"strings([[]byte, []bytev2])",
|
||||||
ve(redis.Strings([]interface{}{[]byte("v1"), []byte("v2")}, nil)),
|
ve(redis.Strings([]interface{}{[]byte("v1"), []byte("v2")}, nil)),
|
||||||
ve([]string{"v1", "v2"}, nil),
|
ve([]string{"v1", "v2"}, nil),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"strings(nil)",
|
"strings([string, string])",
|
||||||
ve(redis.Strings(nil, nil)),
|
ve(redis.Strings([]interface{}{"v1", "v2"}, nil)),
|
||||||
ve([]string(nil), redis.ErrNil),
|
ve([]string{"v1", "v2"}, nil),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"byteslices([v1, v2])",
|
"byteslices([v1, v2])",
|
||||||
@@ -62,9 +82,9 @@ var replyTests = []struct {
|
|||||||
ve([][]byte{[]byte("v1"), []byte("v2")}, nil),
|
ve([][]byte{[]byte("v1"), []byte("v2")}, nil),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"byteslices(nil)",
|
"float64s([v1, v2])",
|
||||||
ve(redis.ByteSlices(nil, nil)),
|
ve(redis.Float64s([]interface{}{[]byte("1.234"), []byte("5.678")}, nil)),
|
||||||
ve([][]byte(nil), redis.ErrNil),
|
ve([]float64{1.234, 5.678}, nil),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"values([v1, v2])",
|
"values([v1, v2])",
|
||||||
@@ -120,6 +140,11 @@ func dial() (redis.Conn, error) {
|
|||||||
return redis.DialDefaultServer()
|
return redis.DialDefaultServer()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// serverAddr wraps DefaultServerAddr() with a more suitable function name for examples.
|
||||||
|
func serverAddr() (string, error) {
|
||||||
|
return redis.DefaultServerAddr()
|
||||||
|
}
|
||||||
|
|
||||||
func ExampleBool() {
|
func ExampleBool() {
|
||||||
c, err := dial()
|
c, err := dial()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
20
vendor/github.com/garyburd/redigo/redis/test_test.go
generated
vendored
20
vendor/github.com/garyburd/redigo/redis/test_test.go
generated
vendored
@@ -38,6 +38,7 @@ var (
|
|||||||
ErrNegativeInt = errNegativeInt
|
ErrNegativeInt = errNegativeInt
|
||||||
|
|
||||||
serverPath = flag.String("redis-server", "redis-server", "Path to redis server binary")
|
serverPath = flag.String("redis-server", "redis-server", "Path to redis server binary")
|
||||||
|
serverAddress = flag.String("redis-address", "127.0.0.1", "The address of the server")
|
||||||
serverBasePort = flag.Int("redis-port", 16379, "Beginning of port range for test servers")
|
serverBasePort = flag.Int("redis-port", 16379, "Beginning of port range for test servers")
|
||||||
serverLogName = flag.String("redis-log", "", "Write Redis server logs to `filename`")
|
serverLogName = flag.String("redis-log", "", "Write Redis server logs to `filename`")
|
||||||
serverLog = ioutil.Discard
|
serverLog = ioutil.Discard
|
||||||
@@ -96,7 +97,8 @@ func (s *Server) watch(r io.Reader, ready chan error) {
|
|||||||
text = scn.Text()
|
text = scn.Text()
|
||||||
fmt.Fprintf(serverLog, "%s\n", text)
|
fmt.Fprintf(serverLog, "%s\n", text)
|
||||||
if !listening {
|
if !listening {
|
||||||
if strings.Contains(text, "The server is now ready to accept connections on port") {
|
if strings.Contains(text, " * Ready to accept connections") ||
|
||||||
|
strings.Contains(text, " * The server is now ready to accept connections on port") {
|
||||||
listening = true
|
listening = true
|
||||||
ready <- nil
|
ready <- nil
|
||||||
}
|
}
|
||||||
@@ -125,28 +127,32 @@ func stopDefaultServer() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// startDefaultServer starts the default server if not already running.
|
// DefaultServerAddr starts the test server if not already started and returns
|
||||||
func startDefaultServer() error {
|
// the address of that server.
|
||||||
|
func DefaultServerAddr() (string, error) {
|
||||||
defaultServerMu.Lock()
|
defaultServerMu.Lock()
|
||||||
defer defaultServerMu.Unlock()
|
defer defaultServerMu.Unlock()
|
||||||
|
addr := fmt.Sprintf("%v:%d", *serverAddress, *serverBasePort)
|
||||||
if defaultServer != nil || defaultServerErr != nil {
|
if defaultServer != nil || defaultServerErr != nil {
|
||||||
return defaultServerErr
|
return addr, defaultServerErr
|
||||||
}
|
}
|
||||||
defaultServer, defaultServerErr = NewServer(
|
defaultServer, defaultServerErr = NewServer(
|
||||||
"default",
|
"default",
|
||||||
"--port", strconv.Itoa(*serverBasePort),
|
"--port", strconv.Itoa(*serverBasePort),
|
||||||
|
"--bind", *serverAddress,
|
||||||
"--save", "",
|
"--save", "",
|
||||||
"--appendonly", "no")
|
"--appendonly", "no")
|
||||||
return defaultServerErr
|
return addr, defaultServerErr
|
||||||
}
|
}
|
||||||
|
|
||||||
// DialDefaultServer starts the test server if not already started and dials a
|
// DialDefaultServer starts the test server if not already started and dials a
|
||||||
// connection to the server.
|
// connection to the server.
|
||||||
func DialDefaultServer() (Conn, error) {
|
func DialDefaultServer() (Conn, error) {
|
||||||
if err := startDefaultServer(); err != nil {
|
addr, err := DefaultServerAddr()
|
||||||
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
c, err := Dial("tcp", fmt.Sprintf(":%d", *serverBasePort), DialReadTimeout(1*time.Second), DialWriteTimeout(1*time.Second))
|
c, err := Dial("tcp", addr, DialReadTimeout(1*time.Second), DialWriteTimeout(1*time.Second))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|||||||
1
vendor/github.com/gin-gonic/autotls/.travis.yml
generated
vendored
1
vendor/github.com/gin-gonic/autotls/.travis.yml
generated
vendored
@@ -4,6 +4,7 @@ go:
|
|||||||
- 1.6.x
|
- 1.6.x
|
||||||
- 1.7.x
|
- 1.7.x
|
||||||
- 1.8.x
|
- 1.8.x
|
||||||
|
- 1.9.x
|
||||||
- master
|
- master
|
||||||
|
|
||||||
git:
|
git:
|
||||||
|
|||||||
2
vendor/github.com/gin-gonic/autotls/doc.go
generated
vendored
2
vendor/github.com/gin-gonic/autotls/doc.go
generated
vendored
@@ -1,4 +1,4 @@
|
|||||||
// Support Let's Encrypt for a Go server application.
|
// Package autotls support Let's Encrypt for a Go server application.
|
||||||
//
|
//
|
||||||
// package main
|
// package main
|
||||||
//
|
//
|
||||||
|
|||||||
2
vendor/github.com/gin-gonic/contrib/README.md
generated
vendored
2
vendor/github.com/gin-gonic/contrib/README.md
generated
vendored
@@ -34,3 +34,5 @@ Each author is responsible of maintaining his own code, although if you submit a
|
|||||||
+ [gin-oauth2](https://github.com/zalando/gin-oauth2) - for working with OAuth2
|
+ [gin-oauth2](https://github.com/zalando/gin-oauth2) - for working with OAuth2
|
||||||
+ [static](https://github.com/hyperboloide/static) An alternative static assets handler for the gin framework.
|
+ [static](https://github.com/hyperboloide/static) An alternative static assets handler for the gin framework.
|
||||||
+ [xss-mw](https://github.com/dvwright/xss-mw) - XssMw is a middleware designed to "auto remove XSS" from user submitted input
|
+ [xss-mw](https://github.com/dvwright/xss-mw) - XssMw is a middleware designed to "auto remove XSS" from user submitted input
|
||||||
|
+ [gin-helmet](https://github.com/danielkov/gin-helmet) - Collection of simple security middleware.
|
||||||
|
+ [gin-jwt-session](https://github.com/ScottHuangZL/gin-jwt-session) - middleware to provide JWT/Session/Flashes, easy to use while also provide options for adjust if necessary. Provide sample too.
|
||||||
|
|||||||
2
vendor/github.com/gin-gonic/contrib/ginrus/example/example.go
generated
vendored
2
vendor/github.com/gin-gonic/contrib/ginrus/example/example.go
generated
vendored
@@ -5,9 +5,9 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/Sirupsen/logrus"
|
|
||||||
"github.com/gin-gonic/contrib/ginrus"
|
"github.com/gin-gonic/contrib/ginrus"
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
|||||||
8
vendor/github.com/gin-gonic/contrib/ginrus/ginrus.go
generated
vendored
8
vendor/github.com/gin-gonic/contrib/ginrus/ginrus.go
generated
vendored
@@ -6,10 +6,14 @@ package ginrus
|
|||||||
import (
|
import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/Sirupsen/logrus"
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type loggerEntryWithFields interface {
|
||||||
|
WithFields(fields logrus.Fields) *logrus.Entry
|
||||||
|
}
|
||||||
|
|
||||||
// Ginrus returns a gin.HandlerFunc (middleware) that logs requests using logrus.
|
// Ginrus returns a gin.HandlerFunc (middleware) that logs requests using logrus.
|
||||||
//
|
//
|
||||||
// Requests with errors are logged using logrus.Error().
|
// Requests with errors are logged using logrus.Error().
|
||||||
@@ -18,7 +22,7 @@ import (
|
|||||||
// It receives:
|
// It receives:
|
||||||
// 1. A time package format string (e.g. time.RFC3339).
|
// 1. A time package format string (e.g. time.RFC3339).
|
||||||
// 2. A boolean stating whether to use UTC time zone or local.
|
// 2. A boolean stating whether to use UTC time zone or local.
|
||||||
func Ginrus(logger *logrus.Logger, timeFormat string, utc bool) gin.HandlerFunc {
|
func Ginrus(logger loggerEntryWithFields, timeFormat string, utc bool) gin.HandlerFunc {
|
||||||
return func(c *gin.Context) {
|
return func(c *gin.Context) {
|
||||||
start := time.Now()
|
start := time.Now()
|
||||||
// some evil middlewares modify this values
|
// some evil middlewares modify this values
|
||||||
|
|||||||
6
vendor/github.com/gorilla/sessions/README.md
generated
vendored
6
vendor/github.com/gorilla/sessions/README.md
generated
vendored
@@ -44,14 +44,14 @@ Let's start with an example that shows the sessions API in a nutshell:
|
|||||||
|
|
||||||
First we initialize a session store calling `NewCookieStore()` and passing a
|
First we initialize a session store calling `NewCookieStore()` and passing a
|
||||||
secret key used to authenticate the session. Inside the handler, we call
|
secret key used to authenticate the session. Inside the handler, we call
|
||||||
`store.Get()` to retrieve an existing session or a new one. Then we set some
|
`store.Get()` to retrieve an existing session or create a new one. Then we set
|
||||||
session values in session.Values, which is a `map[interface{}]interface{}`.
|
some session values in session.Values, which is a `map[interface{}]interface{}`.
|
||||||
And finally we call `session.Save()` to save the session in the response.
|
And finally we call `session.Save()` to save the session in the response.
|
||||||
|
|
||||||
Important Note: If you aren't using gorilla/mux, you need to wrap your handlers
|
Important Note: If you aren't using gorilla/mux, you need to wrap your handlers
|
||||||
with
|
with
|
||||||
[`context.ClearHandler`](http://www.gorillatoolkit.org/pkg/context#ClearHandler)
|
[`context.ClearHandler`](http://www.gorillatoolkit.org/pkg/context#ClearHandler)
|
||||||
as or else you will leak memory! An easy way to do this is to wrap the top-level
|
or else you will leak memory! An easy way to do this is to wrap the top-level
|
||||||
mux when calling http.ListenAndServe:
|
mux when calling http.ListenAndServe:
|
||||||
|
|
||||||
```go
|
```go
|
||||||
|
|||||||
4
vendor/github.com/gorilla/sessions/store_test.go
generated
vendored
4
vendor/github.com/gorilla/sessions/store_test.go
generated
vendored
@@ -59,6 +59,10 @@ func TestGH2MaxLength(t *testing.T) {
|
|||||||
w := httptest.NewRecorder()
|
w := httptest.NewRecorder()
|
||||||
|
|
||||||
session, err := store.New(req, "my session")
|
session, err := store.New(req, "my session")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal("failed to create session", err)
|
||||||
|
}
|
||||||
|
|
||||||
session.Values["big"] = make([]byte, base64.StdEncoding.DecodedLen(4096*2))
|
session.Values["big"] = make([]byte, base64.StdEncoding.DecodedLen(4096*2))
|
||||||
err = session.Save(req, w)
|
err = session.Save(req, w)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
|
|||||||
4
vendor/github.com/mattn/go-isatty/.travis.yml
generated
vendored
4
vendor/github.com/mattn/go-isatty/.travis.yml
generated
vendored
@@ -2,6 +2,10 @@ language: go
|
|||||||
go:
|
go:
|
||||||
- tip
|
- tip
|
||||||
|
|
||||||
|
os:
|
||||||
|
- linux
|
||||||
|
- osx
|
||||||
|
|
||||||
before_install:
|
before_install:
|
||||||
- go get github.com/mattn/goveralls
|
- go get github.com/mattn/goveralls
|
||||||
- go get golang.org/x/tools/cmd/cover
|
- go get golang.org/x/tools/cmd/cover
|
||||||
|
|||||||
2
vendor/github.com/mattn/go-isatty/isatty_linux.go
generated
vendored
2
vendor/github.com/mattn/go-isatty/isatty_linux.go
generated
vendored
@@ -1,5 +1,5 @@
|
|||||||
// +build linux
|
// +build linux
|
||||||
// +build !appengine
|
// +build !appengine,!ppc64,!ppc64le
|
||||||
|
|
||||||
package isatty
|
package isatty
|
||||||
|
|
||||||
|
|||||||
19
vendor/github.com/mattn/go-isatty/isatty_linux_ppc64x.go
generated
vendored
Normal file
19
vendor/github.com/mattn/go-isatty/isatty_linux_ppc64x.go
generated
vendored
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
// +build linux
|
||||||
|
// +build ppc64 ppc64le
|
||||||
|
|
||||||
|
package isatty
|
||||||
|
|
||||||
|
import (
|
||||||
|
"unsafe"
|
||||||
|
|
||||||
|
syscall "golang.org/x/sys/unix"
|
||||||
|
)
|
||||||
|
|
||||||
|
const ioctlReadTermios = syscall.TCGETS
|
||||||
|
|
||||||
|
// IsTerminal return true if the file descriptor is terminal.
|
||||||
|
func IsTerminal(fd uintptr) bool {
|
||||||
|
var termios syscall.Termios
|
||||||
|
_, _, err := syscall.Syscall6(syscall.SYS_IOCTL, fd, ioctlReadTermios, uintptr(unsafe.Pointer(&termios)), 0, 0, 0)
|
||||||
|
return err == 0
|
||||||
|
}
|
||||||
2
vendor/github.com/mattn/go-isatty/isatty_others.go
generated
vendored
2
vendor/github.com/mattn/go-isatty/isatty_others.go
generated
vendored
@@ -3,7 +3,7 @@
|
|||||||
|
|
||||||
package isatty
|
package isatty
|
||||||
|
|
||||||
// IsCygwinTerminal() return true if the file descriptor is a cygwin or msys2
|
// IsCygwinTerminal return true if the file descriptor is a cygwin or msys2
|
||||||
// terminal. This is also always false on this environment.
|
// terminal. This is also always false on this environment.
|
||||||
func IsCygwinTerminal(fd uintptr) bool {
|
func IsCygwinTerminal(fd uintptr) bool {
|
||||||
return false
|
return false
|
||||||
|
|||||||
6
vendor/github.com/qiniu/api.v7/.travis.yml
generated
vendored
6
vendor/github.com/qiniu/api.v7/.travis.yml
generated
vendored
@@ -6,14 +6,8 @@ go:
|
|||||||
env:
|
env:
|
||||||
global:
|
global:
|
||||||
- QINIU_KODO_TEST=1
|
- QINIU_KODO_TEST=1
|
||||||
- QINIU_ACCESS_KEY="QWYn5TFQsLLU1pL5MFEmX3s5DmHdUThav9WyOWOm"
|
|
||||||
- QINIU_SECRET_KEY="Bxckh6FA-Fbs9Yt3i3cbKVK22UPBmAOHJcL95pGz"
|
|
||||||
- QINIU_TEST_BUCKET="gosdk"
|
|
||||||
- QINIU_TEST_DOMAIN="gosdk.qiniudn.com"
|
|
||||||
|
|
||||||
install:
|
install:
|
||||||
- export QINIU_SRC=$HOME/gopath/src
|
- export QINIU_SRC=$HOME/gopath/src
|
||||||
- mkdir -p $QINIU_SRC/github.com/qiniu
|
- mkdir -p $QINIU_SRC/github.com/qiniu
|
||||||
- export TRAVIS_BUILD_DIR=$QINIU_SRC/github.com/qiniu/api.v7
|
|
||||||
- cd $TRAVIS_BUILD_DIR
|
|
||||||
- go get github.com/qiniu/x
|
- go get github.com/qiniu/x
|
||||||
19
vendor/github.com/qiniu/api.v7/CHANGELOG.md
generated
vendored
19
vendor/github.com/qiniu/api.v7/CHANGELOG.md
generated
vendored
@@ -1,4 +1,21 @@
|
|||||||
#Changelog
|
# Changelog
|
||||||
|
|
||||||
|
# 7.2.3 (2017-09-25)
|
||||||
|
* 增加Qiniu的鉴权方式
|
||||||
|
* 删除prefop域名检测功能
|
||||||
|
* 暴露分片上传的接口以支持复杂的自定义业务逻辑
|
||||||
|
|
||||||
|
## 7.2.2 (2017-09-19)
|
||||||
|
* 为表单上传和分片上传增加代理支持
|
||||||
|
* 优化表单上传的crc32计算方式,减少内存消耗
|
||||||
|
* 增加网页图片的Base64上传方式
|
||||||
|
|
||||||
|
## 7.2.1 (2017-08-20)
|
||||||
|
* 设置FormUpload默认支持crc32校验
|
||||||
|
* ResumeUpload从API层面即支持crc32校验
|
||||||
|
|
||||||
|
## 7.2.0 (2017-07-28)
|
||||||
|
* 重构了v7 SDK的所有代码
|
||||||
|
|
||||||
## 7.1.0 (2016-6-22)
|
## 7.1.0 (2016-6-22)
|
||||||
|
|
||||||
|
|||||||
5
vendor/github.com/qiniu/api.v7/Makefile
generated
vendored
Normal file
5
vendor/github.com/qiniu/api.v7/Makefile
generated
vendored
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
test:
|
||||||
|
go test -v ./auth/...
|
||||||
|
go test -v ./conf/...
|
||||||
|
go test -v ./cdn/...
|
||||||
|
go test -v ./storage/...
|
||||||
15
vendor/github.com/qiniu/api.v7/README.md
generated
vendored
15
vendor/github.com/qiniu/api.v7/README.md
generated
vendored
@@ -1,7 +1,7 @@
|
|||||||
github.com/qiniu/api.v7 (Qiniu Go SDK v7.x)
|
github.com/qiniu/api.v7 (Qiniu Go SDK v7.x)
|
||||||
===============
|
===============
|
||||||
|
|
||||||
[](https://travis-ci.org/qiniu/api.v7) [](https://godoc.org/github.com/qiniu/api.v7)
|
[](https://travis-ci.org/qiniu/api.v7) [](https://godoc.org/github.com/qiniu/api.v7)
|
||||||
|
|
||||||
[](http://qiniu.com/)
|
[](http://qiniu.com/)
|
||||||
|
|
||||||
@@ -10,16 +10,11 @@ github.com/qiniu/api.v7 (Qiniu Go SDK v7.x)
|
|||||||
```
|
```
|
||||||
go get -u github.com/qiniu/api.v7
|
go get -u github.com/qiniu/api.v7
|
||||||
```
|
```
|
||||||
如果碰到golang.org/x/net/context 不能下载,请把 http://devtools.qiniu.com/golang.org.x.net.context.tgz 下载到代码目录下并解压到src目录,或者直接下载全部 http://devtools.qiniu.com/qiniu_api_v7.tgz。
|
|
||||||
|
|
||||||
# 使用文档
|
# 文档
|
||||||
|
|
||||||
## KODO Blob Storage (七牛对象存储)
|
[七牛SDK文档站](https://developer.qiniu.com/kodo/sdk/1238/go) 或者 [项目WIKI](https://github.com/qiniu/api.v7/wiki)
|
||||||
|
|
||||||
* [github.com/qiniu/api.v7/kodo](http://godoc.org/github.com/qiniu/api.v7/kodo)
|
# 示例
|
||||||
* [github.com/qiniu/api.v7/kodocli](http://godoc.org/github.com/qiniu/api.v7/kodocli)
|
|
||||||
|
|
||||||
如果您是在业务服务器(服务器端)调用七牛云存储的服务,请使用 [github.com/qiniu/api.v7/kodo](http://godoc.org/github.com/qiniu/api.v7/kodo)。
|
|
||||||
|
|
||||||
如果您是在客户端(比如:Android/iOS 设备、Windows/Mac/Linux 桌面环境)调用七牛云存储的服务,请使用 [github.com/qiniu/api.v7/kodocli](http://godoc.org/github.com/qiniu/api.v7/kodocli)。注意,在这种场合下您不应该在任何地方配置 AccessKey/SecretKey。泄露 AccessKey/SecretKey 如同泄露您的用户名/密码一样十分危险,会影响您的数据安全。
|
|
||||||
|
|
||||||
|
[参考代码](https://github.com/qiniu/api.v7/tree/master/examples)
|
||||||
|
|||||||
99
vendor/github.com/qiniu/api.v7/api/api.go
generated
vendored
99
vendor/github.com/qiniu/api.v7/api/api.go
generated
vendored
@@ -1,99 +0,0 @@
|
|||||||
package api
|
|
||||||
|
|
||||||
import (
|
|
||||||
. "context"
|
|
||||||
"sync"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/qiniu/x/rpc.v7"
|
|
||||||
)
|
|
||||||
|
|
||||||
const DefaultApiHost string = "http://uc.qbox.me"
|
|
||||||
|
|
||||||
var (
|
|
||||||
bucketMu sync.RWMutex
|
|
||||||
bucketCache = make(map[string]BucketInfo)
|
|
||||||
)
|
|
||||||
|
|
||||||
type Client struct {
|
|
||||||
*rpc.Client
|
|
||||||
host string
|
|
||||||
scheme string
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewClient(host string, scheme string) *Client {
|
|
||||||
if host == "" {
|
|
||||||
host = DefaultApiHost
|
|
||||||
}
|
|
||||||
client := rpc.DefaultClient
|
|
||||||
return &Client{&client, host, scheme}
|
|
||||||
}
|
|
||||||
|
|
||||||
type BucketInfo struct {
|
|
||||||
UpHosts []string `json:"up"`
|
|
||||||
IoHost string `json:"io"`
|
|
||||||
Expire int64 `json:"expire"` // expire == 0 means no expire
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *Client) GetBucketInfo(ak, bucketName string) (ret BucketInfo, err error) {
|
|
||||||
key := ak + ":" + bucketName + ":" + p.scheme
|
|
||||||
bucketMu.RLock()
|
|
||||||
bucketInfo, ok := bucketCache[key]
|
|
||||||
bucketMu.RUnlock()
|
|
||||||
if ok && (bucketInfo.Expire == 0 || bucketInfo.Expire > time.Now().Unix()) {
|
|
||||||
ret = bucketInfo
|
|
||||||
return
|
|
||||||
}
|
|
||||||
hostInfo, err := p.bucketHosts(ak, bucketName)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
ret.Expire = time.Now().Unix() + hostInfo.Ttl
|
|
||||||
if p.scheme == "https" {
|
|
||||||
ret.UpHosts = hostInfo.Https["up"]
|
|
||||||
if iohosts, ok := hostInfo.Https["io"]; ok && len(iohosts) != 0 {
|
|
||||||
ret.IoHost = iohosts[0]
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
ret.UpHosts = hostInfo.Http["up"]
|
|
||||||
if iohosts, ok := hostInfo.Http["io"]; ok && len(iohosts) != 0 {
|
|
||||||
ret.IoHost = iohosts[0]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
bucketMu.Lock()
|
|
||||||
bucketCache[key] = ret
|
|
||||||
bucketMu.Unlock()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
type HostsInfo struct {
|
|
||||||
Ttl int64 `json:"ttl"`
|
|
||||||
Http map[string][]string `json:"http"`
|
|
||||||
Https map[string][]string `json:"https"`
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
请求包:
|
|
||||||
GET /v1/query?ak=<ak>&&bucket=<bucket>
|
|
||||||
返回包:
|
|
||||||
200 OK {
|
|
||||||
"ttl": <ttl>, // 有效时间
|
|
||||||
"http": {
|
|
||||||
"up": [],
|
|
||||||
"io": [], // 当bucket为global时,我们不需要iohost, io缺省
|
|
||||||
},
|
|
||||||
"https": {
|
|
||||||
"up": [],
|
|
||||||
"io": [], // 当bucket为global时,我们不需要iohost, io缺省
|
|
||||||
}
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
func (p *Client) bucketHosts(ak, bucket string) (info HostsInfo, err error) {
|
|
||||||
ctx := Background()
|
|
||||||
err = p.CallWithForm(ctx, &info, "GET", p.host+"/v1/query", map[string][]string{
|
|
||||||
"ak": []string{ak},
|
|
||||||
"bucket": []string{bucket},
|
|
||||||
})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
2
vendor/github.com/qiniu/api.v7/auth/qbox/doc.go
generated
vendored
Normal file
2
vendor/github.com/qiniu/api.v7/auth/qbox/doc.go
generated
vendored
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
// qbox 包提供了该SDK需要的相关鉴权方法
|
||||||
|
package qbox
|
||||||
173
vendor/github.com/qiniu/api.v7/auth/qbox/qbox_auth.go
generated
vendored
173
vendor/github.com/qiniu/api.v7/auth/qbox/qbox_auth.go
generated
vendored
@@ -4,61 +4,45 @@ import (
|
|||||||
"crypto/hmac"
|
"crypto/hmac"
|
||||||
"crypto/sha1"
|
"crypto/sha1"
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
. "github.com/qiniu/api.v7/conf"
|
|
||||||
"github.com/qiniu/x/bytes.v7/seekable"
|
"github.com/qiniu/x/bytes.v7/seekable"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ----------------------------------------------------------
|
// Mac 七牛AK/SK的对象,AK/SK可以从 https://portal.qiniu.com/user/key 获取。
|
||||||
|
|
||||||
type Mac struct {
|
type Mac struct {
|
||||||
AccessKey string
|
AccessKey string
|
||||||
SecretKey []byte
|
SecretKey []byte
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NewMac 构建一个新的拥有AK/SK的对象
|
||||||
func NewMac(accessKey, secretKey string) (mac *Mac) {
|
func NewMac(accessKey, secretKey string) (mac *Mac) {
|
||||||
|
|
||||||
if accessKey == "" {
|
|
||||||
accessKey, secretKey = ACCESS_KEY, SECRET_KEY
|
|
||||||
}
|
|
||||||
return &Mac{accessKey, []byte(secretKey)}
|
return &Mac{accessKey, []byte(secretKey)}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Sign 对数据进行签名,一般用于私有空间下载用途
|
||||||
func (mac *Mac) Sign(data []byte) (token string) {
|
func (mac *Mac) Sign(data []byte) (token string) {
|
||||||
|
|
||||||
h := hmac.New(sha1.New, mac.SecretKey)
|
h := hmac.New(sha1.New, mac.SecretKey)
|
||||||
h.Write(data)
|
h.Write(data)
|
||||||
|
|
||||||
sign := base64.URLEncoding.EncodeToString(h.Sum(nil))
|
sign := base64.URLEncoding.EncodeToString(h.Sum(nil))
|
||||||
return mac.AccessKey + ":" + sign[:27]
|
return fmt.Sprintf("%s:%s", mac.AccessKey, sign)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SignWithData 对数据进行签名,一般用于上传凭证的生成用途
|
||||||
func (mac *Mac) SignWithData(b []byte) (token string) {
|
func (mac *Mac) SignWithData(b []byte) (token string) {
|
||||||
|
encodedData := base64.URLEncoding.EncodeToString(b)
|
||||||
blen := base64.URLEncoding.EncodedLen(len(b))
|
|
||||||
|
|
||||||
key := mac.AccessKey
|
|
||||||
nkey := len(key)
|
|
||||||
ret := make([]byte, nkey+30+blen)
|
|
||||||
|
|
||||||
base64.URLEncoding.Encode(ret[nkey+30:], b)
|
|
||||||
|
|
||||||
h := hmac.New(sha1.New, mac.SecretKey)
|
h := hmac.New(sha1.New, mac.SecretKey)
|
||||||
h.Write(ret[nkey+30:])
|
h.Write([]byte(encodedData))
|
||||||
digest := h.Sum(nil)
|
digest := h.Sum(nil)
|
||||||
|
sign := base64.URLEncoding.EncodeToString(digest)
|
||||||
copy(ret, key)
|
return fmt.Sprintf("%s:%s:%s", mac.AccessKey, sign, encodedData)
|
||||||
ret[nkey] = ':'
|
|
||||||
base64.URLEncoding.Encode(ret[nkey+1:], digest)
|
|
||||||
ret[nkey+29] = ':'
|
|
||||||
|
|
||||||
return string(ret)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (mac *Mac) SignRequest(req *http.Request, incbody bool) (token string, err error) {
|
// SignRequest 对数据进行签名,一般用于管理凭证的生成
|
||||||
|
func (mac *Mac) SignRequest(req *http.Request) (token string, err error) {
|
||||||
h := hmac.New(sha1.New, mac.SecretKey)
|
h := hmac.New(sha1.New, mac.SecretKey)
|
||||||
|
|
||||||
u := req.URL
|
u := req.URL
|
||||||
@@ -68,7 +52,7 @@ func (mac *Mac) SignRequest(req *http.Request, incbody bool) (token string, err
|
|||||||
}
|
}
|
||||||
io.WriteString(h, data+"\n")
|
io.WriteString(h, data+"\n")
|
||||||
|
|
||||||
if incbody {
|
if incBody(req) {
|
||||||
s2, err2 := seekable.New(req)
|
s2, err2 := seekable.New(req)
|
||||||
if err2 != nil {
|
if err2 != nil {
|
||||||
return "", err2
|
return "", err2
|
||||||
@@ -77,18 +61,74 @@ func (mac *Mac) SignRequest(req *http.Request, incbody bool) (token string, err
|
|||||||
}
|
}
|
||||||
|
|
||||||
sign := base64.URLEncoding.EncodeToString(h.Sum(nil))
|
sign := base64.URLEncoding.EncodeToString(h.Sum(nil))
|
||||||
token = mac.AccessKey + ":" + sign
|
token = fmt.Sprintf("%s:%s", mac.AccessKey, sign)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (mac *Mac) VerifyCallback(req *http.Request) (bool, error) {
|
// SignRequestV2 对数据进行签名,一般用于高级管理凭证的生成
|
||||||
|
func (mac *Mac) SignRequestV2(req *http.Request) (token string, err error) {
|
||||||
|
h := hmac.New(sha1.New, mac.SecretKey)
|
||||||
|
|
||||||
|
u := req.URL
|
||||||
|
|
||||||
|
//write method path?query
|
||||||
|
io.WriteString(h, fmt.Sprintf("%s %s", req.Method, u.Path))
|
||||||
|
if u.RawQuery != "" {
|
||||||
|
io.WriteString(h, "?")
|
||||||
|
io.WriteString(h, u.RawQuery)
|
||||||
|
}
|
||||||
|
|
||||||
|
//write host and posrt
|
||||||
|
io.WriteString(h, "\nHost: ")
|
||||||
|
io.WriteString(h, req.Host)
|
||||||
|
if req.URL.Port() != "" {
|
||||||
|
io.WriteString(h, ":")
|
||||||
|
io.WriteString(h, req.URL.Port())
|
||||||
|
}
|
||||||
|
|
||||||
|
//write content type
|
||||||
|
contentType := req.Header.Get("Content-Type")
|
||||||
|
if contentType != "" {
|
||||||
|
io.WriteString(h, "\n")
|
||||||
|
io.WriteString(h, fmt.Sprintf("Content-Type: %s", contentType))
|
||||||
|
}
|
||||||
|
|
||||||
|
io.WriteString(h, "\n\n")
|
||||||
|
|
||||||
|
//write body
|
||||||
|
if incBodyV2(req) {
|
||||||
|
s2, err2 := seekable.New(req)
|
||||||
|
if err2 != nil {
|
||||||
|
return "", err2
|
||||||
|
}
|
||||||
|
h.Write(s2.Bytes())
|
||||||
|
}
|
||||||
|
|
||||||
|
sign := base64.URLEncoding.EncodeToString(h.Sum(nil))
|
||||||
|
token = fmt.Sprintf("%s:%s", mac.AccessKey, sign)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 管理凭证生成时,是否同时对request body进行签名
|
||||||
|
func incBody(req *http.Request) bool {
|
||||||
|
return req.Body != nil &&
|
||||||
|
req.Header.Get("Content-Type") == "application/x-www-form-urlencoded"
|
||||||
|
}
|
||||||
|
|
||||||
|
func incBodyV2(req *http.Request) bool {
|
||||||
|
contentType := req.Header.Get("Content-Type")
|
||||||
|
return req.Body != nil && (contentType == "application/x-www-form-urlencoded" ||
|
||||||
|
contentType == "application/json")
|
||||||
|
}
|
||||||
|
|
||||||
|
// VerifyCallback 验证上传回调请求是否来自七牛
|
||||||
|
func (mac *Mac) VerifyCallback(req *http.Request) (bool, error) {
|
||||||
auth := req.Header.Get("Authorization")
|
auth := req.Header.Get("Authorization")
|
||||||
if auth == "" {
|
if auth == "" {
|
||||||
return false, nil
|
return false, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
token, err := mac.SignRequest(req, true)
|
token, err := mac.SignRequest(req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
@@ -96,76 +136,17 @@ func (mac *Mac) VerifyCallback(req *http.Request) (bool, error) {
|
|||||||
return auth == "QBox "+token, nil
|
return auth == "QBox "+token, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------------------
|
// Sign 一般用于下载凭证的签名
|
||||||
|
|
||||||
func Sign(mac *Mac, data []byte) string {
|
func Sign(mac *Mac, data []byte) string {
|
||||||
|
|
||||||
if mac == nil {
|
|
||||||
mac = NewMac(ACCESS_KEY, SECRET_KEY)
|
|
||||||
}
|
|
||||||
return mac.Sign(data)
|
return mac.Sign(data)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SignWithData 一般用于上传凭证的签名
|
||||||
func SignWithData(mac *Mac, data []byte) string {
|
func SignWithData(mac *Mac, data []byte) string {
|
||||||
|
|
||||||
if mac == nil {
|
|
||||||
mac = NewMac(ACCESS_KEY, SECRET_KEY)
|
|
||||||
}
|
|
||||||
return mac.SignWithData(data)
|
return mac.SignWithData(data)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------------------
|
// VerifyCallback 验证上传回调请求是否来自七牛
|
||||||
|
func VerifyCallback(mac *Mac, req *http.Request) (bool, error) {
|
||||||
type Transport struct {
|
return mac.VerifyCallback(req)
|
||||||
mac Mac
|
|
||||||
Transport http.RoundTripper
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func incBody(req *http.Request) bool {
|
|
||||||
|
|
||||||
if req.Body == nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
if ct, ok := req.Header["Content-Type"]; ok {
|
|
||||||
switch ct[0] {
|
|
||||||
case "application/x-www-form-urlencoded":
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *Transport) NestedObject() interface{} {
|
|
||||||
|
|
||||||
return t.Transport
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *Transport) RoundTrip(req *http.Request) (resp *http.Response, err error) {
|
|
||||||
|
|
||||||
token, err := t.mac.SignRequest(req, incBody(req))
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
req.Header.Set("Authorization", "QBox "+token)
|
|
||||||
return t.Transport.RoundTrip(req)
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewTransport(mac *Mac, transport http.RoundTripper) *Transport {
|
|
||||||
|
|
||||||
if mac == nil {
|
|
||||||
mac = NewMac(ACCESS_KEY, SECRET_KEY)
|
|
||||||
}
|
|
||||||
if transport == nil {
|
|
||||||
transport = http.DefaultTransport
|
|
||||||
}
|
|
||||||
t := &Transport{mac: *mac, Transport: transport}
|
|
||||||
return t
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewClient(mac *Mac, transport http.RoundTripper) *http.Client {
|
|
||||||
|
|
||||||
t := NewTransport(mac, transport)
|
|
||||||
return &http.Client{Transport: t}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------------------
|
|
||||||
|
|||||||
8
vendor/github.com/qiniu/api.v7/cdn/anti_leech.go
generated
vendored
8
vendor/github.com/qiniu/api.v7/cdn/anti_leech.go
generated
vendored
@@ -7,10 +7,9 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
// CreateTimestampAntileechURL 构建带时间戳防盗链的链接
|
// CreateTimestampAntileechURL 用来构建七牛CDN时间戳防盗链的访问链接
|
||||||
// encryptKey 七牛防盗链key
|
func CreateTimestampAntileechURL(urlStr string, encryptKey string,
|
||||||
func CreateTimestampAntileechURL(urlStr string, encryptKey string, durationInSeconds int64) (antileechURL string, err error) {
|
durationInSeconds int64) (antileechURL string, err error) {
|
||||||
|
|
||||||
u, err := url.Parse(urlStr)
|
u, err := url.Parse(urlStr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
@@ -27,7 +26,6 @@ func CreateTimestampAntileechURL(urlStr string, encryptKey string, durationInSec
|
|||||||
if u.RawQuery == "" {
|
if u.RawQuery == "" {
|
||||||
antileechURL = u.String() + "?" + q.Encode()
|
antileechURL = u.String() + "?" + q.Encode()
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
antileechURL = u.String() + "&" + q.Encode()
|
antileechURL = u.String() + "&" + q.Encode()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
11
vendor/github.com/qiniu/api.v7/cdn/anti_leech_test.go
generated
vendored
11
vendor/github.com/qiniu/api.v7/cdn/anti_leech_test.go
generated
vendored
@@ -6,7 +6,7 @@ import (
|
|||||||
|
|
||||||
func TestCreateTimestampAntiLeech(t *testing.T) {
|
func TestCreateTimestampAntiLeech(t *testing.T) {
|
||||||
type args struct {
|
type args struct {
|
||||||
urlStr string
|
urlStr string
|
||||||
encryptKey string
|
encryptKey string
|
||||||
durationInSeconds int64
|
durationInSeconds int64
|
||||||
}
|
}
|
||||||
@@ -18,20 +18,21 @@ func TestCreateTimestampAntiLeech(t *testing.T) {
|
|||||||
{
|
{
|
||||||
name: "antileech_1",
|
name: "antileech_1",
|
||||||
args: args{
|
args: args{
|
||||||
urlStr: "http://www.abc.com/abc.jpg?stat",
|
urlStr: "http://www.example.com/testfile.jpg",
|
||||||
encryptKey: "abc",
|
encryptKey: "abc123",
|
||||||
durationInSeconds: 20,
|
durationInSeconds: 3600,
|
||||||
},
|
},
|
||||||
wantErr: false,
|
wantErr: false,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
_, err := CreateTimestampAntileechURL(tt.args.urlStr, tt.args.encryptKey, tt.args.durationInSeconds)
|
targetUrl, err := CreateTimestampAntileechURL(tt.args.urlStr, tt.args.encryptKey, tt.args.durationInSeconds)
|
||||||
if (err != nil) != tt.wantErr {
|
if (err != nil) != tt.wantErr {
|
||||||
t.Errorf("CreateTimestampAntiLeech() error = %v, wantErr %v", err, tt.wantErr)
|
t.Errorf("CreateTimestampAntiLeech() error = %v, wantErr %v", err, tt.wantErr)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
t.Log(targetUrl)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
221
vendor/github.com/qiniu/api.v7/cdn/api.go
generated
vendored
221
vendor/github.com/qiniu/api.v7/cdn/api.go
generated
vendored
@@ -3,73 +3,72 @@ package cdn
|
|||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/qiniu/api.v7/auth/qbox"
|
"github.com/qiniu/api.v7/auth/qbox"
|
||||||
. "github.com/qiniu/api.v7/conf"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Fusion CDN服务域名
|
||||||
var (
|
var (
|
||||||
FUSION_HOST = "http://fusion.qiniuapi.com"
|
FusionHost = "http://fusion.qiniuapi.com"
|
||||||
)
|
)
|
||||||
|
|
||||||
/* TrafficReqBody
|
// CdnManager 提供了文件和目录刷新,文件预取,获取域名带宽和流量数据,获取域名日志列表等功能
|
||||||
|
type CdnManager struct {
|
||||||
|
mac *qbox.Mac
|
||||||
|
}
|
||||||
|
|
||||||
批量查询带宽/流量 请求内容
|
// NewCdnManager 用来构建一个新的 CdnManager
|
||||||
|
func NewCdnManager(mac *qbox.Mac) *CdnManager {
|
||||||
|
return &CdnManager{mac: mac}
|
||||||
|
}
|
||||||
|
|
||||||
StartDate string 开始日期,例如:2016-07-01
|
// TrafficReq 为批量查询带宽/流量的API请求内容
|
||||||
EndDate string 结束日期,例如:2016-07-03
|
// StartDate 开始日期,格式例如:2016-07-01
|
||||||
Granularity string 粒度,取值:5min / hour /day
|
// EndDate 结束日期,格式例如:2016-07-03
|
||||||
Domains string 域名列表,以 ;分割
|
// Granularity 取值粒度,取值可选值:5min/hour/day
|
||||||
*/
|
// Domains 域名列表,彼此用 ; 连接
|
||||||
type TrafficReqBody struct {
|
type TrafficReq struct {
|
||||||
StartDate string `json:"startDate"`
|
StartDate string `json:"startDate"`
|
||||||
EndDate string `json:"endDate"`
|
EndDate string `json:"endDate"`
|
||||||
Granularity string `json:"granularity"`
|
Granularity string `json:"granularity"`
|
||||||
Domains string `json:"domains"`
|
Domains string `json:"domains"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// TrafficResp
|
// TrafficResp 为带宽/流量查询响应内容
|
||||||
// 带宽/流量查询响应内容
|
|
||||||
type TrafficResp struct {
|
type TrafficResp struct {
|
||||||
Code int `json:"code"`
|
Code int `json:"code"`
|
||||||
Error string `json:"error"`
|
Error string `json:"error"`
|
||||||
Time []string `json:"time,omitempty"`
|
Time []string `json:"time,omitempty"`
|
||||||
Data map[string]TrafficRespData `json:"data,omitempty"`
|
Data map[string]TrafficData `json:"data,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// TrafficRespData
|
// TrafficData 为带宽/流量数据
|
||||||
// 带宽/流量数据
|
type TrafficData struct {
|
||||||
type TrafficRespData struct {
|
|
||||||
DomainChina []int `json:"china"`
|
DomainChina []int `json:"china"`
|
||||||
DomainOversea []int `json:"oversea"`
|
DomainOversea []int `json:"oversea"`
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
// GetBandwidthData 方法用来获取域名访问带宽数据
|
||||||
// BandWidth
|
// StartDate string 必须 开始日期,例如:2016-07-01
|
||||||
|
// EndDate string 必须 结束日期,例如:2016-07-03
|
||||||
获取域名访问带宽数据
|
// Granularity string 必须 粒度,取值:5min / hour /day
|
||||||
http://developer.qiniu.com/article/fusion/api/traffic-bandwidth.html
|
// Domains []string 必须 域名列表
|
||||||
|
func (m *CdnManager) GetBandwidthData(startDate, endDate, granularity string,
|
||||||
StartDate string 必须 开始日期,例如:2016-07-01
|
domainList []string) (bandwidthData TrafficResp, err error) {
|
||||||
EndDate string 必须 结束日期,例如:2016-07-03
|
|
||||||
Granularity string 必须 粒度,取值:5min / hour /day
|
|
||||||
Domains []string 必须 域名列表
|
|
||||||
*/
|
|
||||||
func GetBandWidthData(startDate, endDate, granularity string, domainList []string) (bandwidthData TrafficResp, err error) {
|
|
||||||
|
|
||||||
domains := strings.Join(domainList, ";")
|
domains := strings.Join(domainList, ";")
|
||||||
reqBody := TrafficReqBody{
|
reqBody := TrafficReq{
|
||||||
StartDate: startDate,
|
StartDate: startDate,
|
||||||
EndDate: endDate,
|
EndDate: endDate,
|
||||||
Granularity: granularity,
|
Granularity: granularity,
|
||||||
Domains: domains,
|
Domains: domains,
|
||||||
}
|
}
|
||||||
|
|
||||||
resData, reqErr := postRequest("v2/tune/bandwidth", reqBody)
|
resData, reqErr := postRequest(m.mac, "/v2/tune/bandwidth", reqBody)
|
||||||
if reqErr != nil {
|
if reqErr != nil {
|
||||||
err = reqErr
|
err = reqErr
|
||||||
return
|
return
|
||||||
@@ -82,27 +81,22 @@ func GetBandWidthData(startDate, endDate, granularity string, domainList []strin
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Flux
|
// GetFluxData 方法用来获取域名访问流量数据
|
||||||
|
// StartDate string 必须 开始日期,例如:2016-07-01
|
||||||
获取域名访问流量数据
|
// EndDate string 必须 结束日期,例如:2016-07-03
|
||||||
http://developer.qiniu.com/article/fusion/api/traffic-bandwidth.html
|
// Granularity string 必须 粒度,取值:5min / hour /day
|
||||||
|
// Domains []string 必须 域名列表
|
||||||
StartDate string 必须 开始日期,例如:2016-07-01
|
func (m *CdnManager) GetFluxData(startDate, endDate, granularity string,
|
||||||
EndDate string 必须 结束日期,例如:2016-07-03
|
domainList []string) (fluxData TrafficResp, err error) {
|
||||||
Granularity string 必须 粒度,取值:5min / hour /day
|
|
||||||
Domains []string 必须 域名列表
|
|
||||||
*/
|
|
||||||
func GetFluxData(startDate, endDate, granularity string, domainList []string) (fluxData TrafficResp, err error) {
|
|
||||||
|
|
||||||
domains := strings.Join(domainList, ";")
|
domains := strings.Join(domainList, ";")
|
||||||
reqBody := TrafficReqBody{
|
reqBody := TrafficReq{
|
||||||
StartDate: startDate,
|
StartDate: startDate,
|
||||||
EndDate: endDate,
|
EndDate: endDate,
|
||||||
Granularity: granularity,
|
Granularity: granularity,
|
||||||
Domains: domains,
|
Domains: domains,
|
||||||
}
|
}
|
||||||
|
|
||||||
resData, reqErr := postRequest("v2/tune/flux", reqBody)
|
resData, reqErr := postRequest(m.mac, "/v2/tune/flux", reqBody)
|
||||||
if reqErr != nil {
|
if reqErr != nil {
|
||||||
err = reqErr
|
err = reqErr
|
||||||
return
|
return
|
||||||
@@ -117,43 +111,46 @@ func GetFluxData(startDate, endDate, granularity string, domainList []string) (f
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// RefreshReq
|
// RefreshReq 为缓存刷新请求内容
|
||||||
// 缓存刷新请求内容
|
|
||||||
type RefreshReq struct {
|
type RefreshReq struct {
|
||||||
Urls []string `json:"urls"`
|
Urls []string `json:"urls"`
|
||||||
Dirs []string `json:"dirs"`
|
Dirs []string `json:"dirs"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// RefreshResp
|
// RefreshResp 缓存刷新响应内容
|
||||||
// 缓存刷新响应内容
|
|
||||||
type RefreshResp struct {
|
type RefreshResp struct {
|
||||||
Code int `json:"code"`
|
Code int `json:"code"`
|
||||||
Error string `json:"error"`
|
Error string `json:"error"`
|
||||||
RequestID string `json:"requestId,omitempty"`
|
RequestID string `json:"requestId,omitempty"`
|
||||||
InvalidUrls []string `json:"invalidUrls,omitempty"`
|
InvalidUrls []string `json:"invalidUrls,omitempty"`
|
||||||
InvalidDirs []string `json:"invalidDirs,omitempty"`
|
InvalidDirs []string `json:"invalidDirs,omitempty"`
|
||||||
UrlQuotaDay int `json:"urlQuotaDay,omitempty"`
|
URLQuotaDay int `json:"urlQuotaDay,omitempty"`
|
||||||
UrlSurplusDay int `json:"urlSurplusDay,omitempty"`
|
URLSurplusDay int `json:"urlSurplusDay,omitempty"`
|
||||||
DirQuotaDay int `json:"dirQuotaDay,omitempty"`
|
DirQuotaDay int `json:"dirQuotaDay,omitempty"`
|
||||||
DirSurplusDay int `json:"dirSurplusDay,omitempty"`
|
DirSurplusDay int `json:"dirSurplusDay,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
/* RefreshUrlsAndDirs
|
// RefreshUrlsAndDirs 方法用来刷新文件或目录
|
||||||
|
// urls 要刷新的单个url列表,单次方法调用总数不超过100条;单个url,即一个具体的url,
|
||||||
刷新链接列表,每次最多不可以超过100条链接
|
// 例如:http://bar.foo.com/index.html
|
||||||
http://developer.qiniu.com/article/fusion/api/refresh.html
|
// dirs 要刷新的目录url列表,单次方法调用总数不超过10条;目录dir,即表示一个目录级的url,
|
||||||
|
// 例如:http://bar.foo.com/dir/,
|
||||||
urls 要刷新的单个url列表,总数不超过100条;单个url,即一个具体的url,例如:http://bar.foo.com/index.html
|
func (m *CdnManager) RefreshUrlsAndDirs(urls, dirs []string) (result RefreshResp, err error) {
|
||||||
dirs 要刷新的目录url列表,总数不超过10条;目录dir,即表示一个目录级的url,例如:http://bar.foo.com/dir/,也支持在尾部使用通配符,例如:http://bar.foo.com/dir/*
|
if len(urls) > 100 {
|
||||||
*/
|
err = errors.New("urls count exceeds the limit of 100")
|
||||||
func RefreshUrlsAndDirs(urls, dirs []string) (result RefreshResp, err error) {
|
return
|
||||||
|
}
|
||||||
|
if len(dirs) > 10 {
|
||||||
|
err = errors.New("dirs count exceeds the limit of 10")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
reqBody := RefreshReq{
|
reqBody := RefreshReq{
|
||||||
Urls: urls,
|
Urls: urls,
|
||||||
Dirs: dirs,
|
Dirs: dirs,
|
||||||
}
|
}
|
||||||
|
|
||||||
resData, reqErr := postRequest("v2/tune/refresh", reqBody)
|
resData, reqErr := postRequest(m.mac, "/v2/tune/refresh", reqBody)
|
||||||
if reqErr != nil {
|
if reqErr != nil {
|
||||||
err = reqErr
|
err = reqErr
|
||||||
return
|
return
|
||||||
@@ -167,26 +164,22 @@ func RefreshUrlsAndDirs(urls, dirs []string) (result RefreshResp, err error) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// RefreshUrls
|
// RefreshUrls 刷新文件
|
||||||
// 刷新文件
|
func (m *CdnManager) RefreshUrls(urls []string) (result RefreshResp, err error) {
|
||||||
func RefreshUrls(urls []string) (result RefreshResp, err error) {
|
return m.RefreshUrlsAndDirs(urls, nil)
|
||||||
return RefreshUrlsAndDirs(urls, nil)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// RefreshDirs
|
// RefreshDirs 刷新目录
|
||||||
// 刷新目录
|
func (m *CdnManager) RefreshDirs(dirs []string) (result RefreshResp, err error) {
|
||||||
func RefreshDirs(dirs []string) (result RefreshResp, err error) {
|
return m.RefreshUrlsAndDirs(nil, dirs)
|
||||||
return RefreshUrlsAndDirs(nil, dirs)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// PrefetchReq
|
// PrefetchReq 文件预取请求内容
|
||||||
// 文件预取请求内容
|
|
||||||
type PrefetchReq struct {
|
type PrefetchReq struct {
|
||||||
Urls []string `json:"urls"`
|
Urls []string `json:"urls"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// PrefetchResp
|
// PrefetchResp 文件预取响应内容
|
||||||
// 文件预取响应内容
|
|
||||||
type PrefetchResp struct {
|
type PrefetchResp struct {
|
||||||
Code int `json:"code"`
|
Code int `json:"code"`
|
||||||
Error string `json:"error"`
|
Error string `json:"error"`
|
||||||
@@ -196,16 +189,18 @@ type PrefetchResp struct {
|
|||||||
SurplusDay int `json:"surplusDay,omitempty"`
|
SurplusDay int `json:"surplusDay,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// PrefetchUrls
|
// PrefetchUrls 预取文件链接,每次最多不可以超过100条
|
||||||
// 预取文件链接,每次最多不可以超过100条
|
func (m *CdnManager) PrefetchUrls(urls []string) (result PrefetchResp, err error) {
|
||||||
// http://developer.qiniu.com/article/fusion/api/prefetch.html
|
if len(urls) > 100 {
|
||||||
func PrefetchUrls(urls []string) (result PrefetchResp, err error) {
|
err = errors.New("urls count exceeds the limit of 100")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
reqBody := PrefetchReq{
|
reqBody := PrefetchReq{
|
||||||
Urls: urls,
|
Urls: urls,
|
||||||
}
|
}
|
||||||
|
|
||||||
resData, reqErr := postRequest("v2/tune/prefetch", reqBody)
|
resData, reqErr := postRequest(m.mac, "/v2/tune/prefetch", reqBody)
|
||||||
if reqErr != nil {
|
if reqErr != nil {
|
||||||
err = reqErr
|
err = reqErr
|
||||||
return
|
return
|
||||||
@@ -220,12 +215,59 @@ func PrefetchUrls(urls []string) (result PrefetchResp, err error) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// RequestWithBody
|
// ListLogRequest 日志下载请求内容
|
||||||
// 带body对api发出请求并且返回response body
|
type ListLogRequest struct {
|
||||||
func postRequest(path string, body interface{}) (resData []byte, err error) {
|
Day string `json:"day"`
|
||||||
|
Domains string `json:"domains"`
|
||||||
|
}
|
||||||
|
|
||||||
urlStr := fmt.Sprintf("%s/%s", FUSION_HOST, path)
|
// ListLogResult 日志下载相应内容
|
||||||
|
type ListLogResult struct {
|
||||||
|
Code int `json:"code"`
|
||||||
|
Error string `json:"error"`
|
||||||
|
Data map[string][]LogDomainInfo `json:"data"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// LogDomainInfo 日志下载信息
|
||||||
|
type LogDomainInfo struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
Size int64 `json:"size"`
|
||||||
|
ModifiedTime int64 `json:"mtime"`
|
||||||
|
URL string `json:"url"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetCdnLogList 获取CDN域名访问日志的下载链接
|
||||||
|
func (m *CdnManager) GetCdnLogList(day string, domains []string) (
|
||||||
|
listLogResult ListLogResult, err error) {
|
||||||
|
//new log query request
|
||||||
|
logReq := ListLogRequest{
|
||||||
|
Day: day,
|
||||||
|
Domains: strings.Join(domains, ";"),
|
||||||
|
}
|
||||||
|
|
||||||
|
resData, reqErr := postRequest(m.mac, "/v2/tune/log/list", logReq)
|
||||||
|
if reqErr != nil {
|
||||||
|
err = fmt.Errorf("get response error, %s", reqErr)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if decodeErr := json.Unmarshal(resData, &listLogResult); decodeErr != nil {
|
||||||
|
err = fmt.Errorf("get response error, %s", decodeErr)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if listLogResult.Error != "" {
|
||||||
|
err = fmt.Errorf("get log list error, %d %s", listLogResult.Code, listLogResult.Error)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// RequestWithBody 带body对api发出请求并且返回response body
|
||||||
|
func postRequest(mac *qbox.Mac, path string, body interface{}) (resData []byte,
|
||||||
|
err error) {
|
||||||
|
urlStr := fmt.Sprintf("%s%s", FusionHost, path)
|
||||||
reqData, _ := json.Marshal(body)
|
reqData, _ := json.Marshal(body)
|
||||||
req, reqErr := http.NewRequest("POST", urlStr, bytes.NewReader(reqData))
|
req, reqErr := http.NewRequest("POST", urlStr, bytes.NewReader(reqData))
|
||||||
if reqErr != nil {
|
if reqErr != nil {
|
||||||
@@ -233,8 +275,7 @@ func postRequest(path string, body interface{}) (resData []byte, err error) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
mac := qbox.NewMac(ACCESS_KEY, SECRET_KEY)
|
accessToken, signErr := mac.SignRequest(req)
|
||||||
accessToken, signErr := mac.SignRequest(req, false)
|
|
||||||
if signErr != nil {
|
if signErr != nil {
|
||||||
err = signErr
|
err = signErr
|
||||||
return
|
return
|
||||||
@@ -243,9 +284,9 @@ func postRequest(path string, body interface{}) (resData []byte, err error) {
|
|||||||
req.Header.Add("Authorization", "QBox "+accessToken)
|
req.Header.Add("Authorization", "QBox "+accessToken)
|
||||||
req.Header.Add("Content-Type", "application/json")
|
req.Header.Add("Content-Type", "application/json")
|
||||||
|
|
||||||
resp, httpErr := http.DefaultClient.Do(req)
|
resp, respErr := http.DefaultClient.Do(req)
|
||||||
if httpErr != nil {
|
if respErr != nil {
|
||||||
err = httpErr
|
err = respErr
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
defer resp.Body.Close()
|
defer resp.Body.Close()
|
||||||
|
|||||||
280
vendor/github.com/qiniu/api.v7/cdn/api_test.go
generated
vendored
280
vendor/github.com/qiniu/api.v7/cdn/api_test.go
generated
vendored
@@ -1,75 +1,86 @@
|
|||||||
package cdn
|
package cdn
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"math/rand"
|
|
||||||
"os"
|
"os"
|
||||||
"reflect"
|
|
||||||
"strconv"
|
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/qiniu/api.v7/kodo"
|
"github.com/qiniu/api.v7/auth/qbox"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
//global variables
|
||||||
|
|
||||||
var (
|
var (
|
||||||
ak = os.Getenv("QINIU_ACCESS_KEY")
|
ak = os.Getenv("QINIU_ACCESS_KEY")
|
||||||
sk = os.Getenv("QINIU_SECRET_KEY")
|
sk = os.Getenv("QINIU_SECRET_KEY")
|
||||||
domain = os.Getenv("QINIU_TEST_DOMAIN")
|
domain = os.Getenv("QINIU_TEST_DOMAIN")
|
||||||
testBucketName = os.Getenv("QINIU_TEST_BUCKET")
|
|
||||||
|
|
||||||
testDate = time.Now().AddDate(0, 0, -3).Format("2006-01-02")
|
layout = "2006-01-02"
|
||||||
bucket = newBucket()
|
now = time.Now()
|
||||||
client *kodo.Client
|
startDate = now.AddDate(0, 0, -2).Format(layout)
|
||||||
testKey = "fusionTest"
|
endDate = now.AddDate(0, 0, -1).Format(layout)
|
||||||
testURL string
|
logDate = now.AddDate(0, 0, -1).Format(layout)
|
||||||
|
|
||||||
|
testUrls = []string{
|
||||||
|
"http://gosdk.qiniudn.com/qiniu1.png",
|
||||||
|
"http://gosdk.qiniudn.com/qiniu2.png",
|
||||||
|
}
|
||||||
|
testDirs = []string{
|
||||||
|
"http://gosdk.qiniudn.com/dir1/",
|
||||||
|
"http://gosdk.qiniudn.com/dir2/",
|
||||||
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
var mac *qbox.Mac
|
||||||
kodo.SetMac(ak, sk)
|
var cdnManager *CdnManager
|
||||||
rand.Seed(time.Now().UnixNano())
|
|
||||||
testKey += strconv.Itoa(rand.Int())
|
|
||||||
bucket.PutFile(nil, nil, testKey, "doc.go", nil)
|
|
||||||
testURL = domain + "/" + testKey
|
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
if ak == "" || sk == "" {
|
||||||
|
panic("please run ./test-env.sh first")
|
||||||
|
}
|
||||||
|
mac = qbox.NewMac(ak, sk)
|
||||||
|
cdnManager = NewCdnManager(mac)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestGetBandWidthData(t *testing.T) {
|
//TestGetBandwidthData
|
||||||
|
func TestGetBandwidthData(t *testing.T) {
|
||||||
type args struct {
|
type args struct {
|
||||||
startDate string
|
startDate string
|
||||||
endDate string
|
endDate string
|
||||||
granularity string
|
granularity string
|
||||||
domainList []string
|
domainList []string
|
||||||
}
|
}
|
||||||
tests := []struct {
|
|
||||||
name string
|
testCases := []struct {
|
||||||
args args
|
name string
|
||||||
wantTraffic TrafficResp
|
args args
|
||||||
wantErr bool
|
wantCode int
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
name: "BandWidthTest_1",
|
name: "CdnManager_TestGetBandwidthData",
|
||||||
args: args{
|
args: args{
|
||||||
testDate,
|
startDate,
|
||||||
testDate,
|
endDate,
|
||||||
"5min",
|
"5min",
|
||||||
[]string{domain},
|
[]string{domain},
|
||||||
},
|
},
|
||||||
|
wantCode: 200,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
kodo.SetMac(ak, sk)
|
|
||||||
for _, tt := range tests {
|
for _, tc := range testCases {
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
_, err := GetBandWidthData(tt.args.startDate, tt.args.endDate, tt.args.granularity, tt.args.domainList)
|
ret, err := cdnManager.GetBandwidthData(tc.args.startDate, tc.args.endDate,
|
||||||
if (err != nil) != tt.wantErr {
|
tc.args.granularity, tc.args.domainList)
|
||||||
t.Errorf("GetBandWidthData() error = %v, wantErr %v", err, tt.wantErr)
|
if err != nil || ret.Code != tc.wantCode {
|
||||||
|
t.Errorf("GetBandwidth() error = %v, %v", err, ret.Error)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//TestGetFluxData
|
||||||
func TestGetFluxData(t *testing.T) {
|
func TestGetFluxData(t *testing.T) {
|
||||||
type args struct {
|
type args struct {
|
||||||
startDate string
|
startDate string
|
||||||
@@ -77,167 +88,160 @@ func TestGetFluxData(t *testing.T) {
|
|||||||
granularity string
|
granularity string
|
||||||
domainList []string
|
domainList []string
|
||||||
}
|
}
|
||||||
tests := []struct {
|
|
||||||
name string
|
testCases := []struct {
|
||||||
args args
|
name string
|
||||||
wantTraffic TrafficResp
|
args args
|
||||||
wantErr bool
|
wantCode int
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
name: "BandWidthTest_1",
|
name: "CdnManager_TestGetFluxData",
|
||||||
args: args{
|
args: args{
|
||||||
testDate,
|
startDate,
|
||||||
testDate,
|
endDate,
|
||||||
"5min",
|
"5min",
|
||||||
[]string{domain},
|
[]string{domain},
|
||||||
},
|
},
|
||||||
|
wantCode: 200,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
kodo.SetMac(ak, sk)
|
|
||||||
for _, tt := range tests {
|
for _, tc := range testCases {
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
_, err := GetFluxData(tt.args.startDate, tt.args.endDate, tt.args.granularity, tt.args.domainList)
|
ret, err := cdnManager.GetFluxData(tc.args.startDate, tc.args.endDate,
|
||||||
if (err != nil) != tt.wantErr {
|
tc.args.granularity, tc.args.domainList)
|
||||||
t.Errorf("GetFluxData() error = %v, wantErr %v", err, tt.wantErr)
|
if err != nil || ret.Code != tc.wantCode {
|
||||||
|
t.Errorf("GetFlux() error = %v, %v", err, ret.Error)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestRefreshUrlsAndDirs(t *testing.T) {
|
//TestRefreshUrls
|
||||||
kodo.SetMac(ak, sk)
|
|
||||||
|
|
||||||
type args struct {
|
|
||||||
urls []string
|
|
||||||
dirs []string
|
|
||||||
}
|
|
||||||
tests := []struct {
|
|
||||||
name string
|
|
||||||
args args
|
|
||||||
wantResult RefreshResp
|
|
||||||
wantErr bool
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
name: "refresh_test_1",
|
|
||||||
args: args{
|
|
||||||
urls: []string{testURL},
|
|
||||||
},
|
|
||||||
wantErr: false,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, tt := range tests {
|
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
|
||||||
_, err := RefreshUrlsAndDirs(tt.args.urls, tt.args.dirs)
|
|
||||||
if (err != nil) != tt.wantErr {
|
|
||||||
t.Errorf("RefreshUrlsAndDirs() error = %v, wantErr %v", err, tt.wantErr)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestRefreshUrls(t *testing.T) {
|
func TestRefreshUrls(t *testing.T) {
|
||||||
type args struct {
|
type args struct {
|
||||||
urls []string
|
urls []string
|
||||||
}
|
}
|
||||||
tests := []struct {
|
|
||||||
name string
|
testCases := []struct {
|
||||||
args args
|
name string
|
||||||
wantResult RefreshResp
|
args args
|
||||||
wantErr bool
|
wantCode int
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
name: "refresh_test_1",
|
name: "CdnManager_TestRefresUrls",
|
||||||
args: args{
|
args: args{
|
||||||
urls: []string{testURL},
|
urls: testUrls,
|
||||||
},
|
},
|
||||||
wantErr: false,
|
wantCode: 200,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, tt := range tests {
|
for _, tc := range testCases {
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
_, err := RefreshUrls(tt.args.urls)
|
ret, err := cdnManager.RefreshUrls(tc.args.urls)
|
||||||
if (err != nil) != tt.wantErr {
|
if err != nil || ret.Code != tc.wantCode {
|
||||||
t.Errorf("RefreshUrls() error = %v, wantErr %v", err, tt.wantErr)
|
t.Errorf("RefreshUrls() error = %v, %v", err, ret.Error)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//TestRefreshDirs
|
||||||
func TestRefreshDirs(t *testing.T) {
|
func TestRefreshDirs(t *testing.T) {
|
||||||
type args struct {
|
type args struct {
|
||||||
dirs []string
|
dirs []string
|
||||||
}
|
}
|
||||||
tests := []struct {
|
|
||||||
name string
|
testCases := []struct {
|
||||||
args args
|
name string
|
||||||
wantResult RefreshResp
|
args args
|
||||||
wantErr bool
|
wantCode int
|
||||||
}{}
|
}{
|
||||||
for _, tt := range tests {
|
{
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
name: "CdnManager_TestRefreshDirs",
|
||||||
gotResult, err := RefreshDirs(tt.args.dirs)
|
args: args{
|
||||||
if (err != nil) != tt.wantErr {
|
dirs: testDirs,
|
||||||
t.Errorf("RefreshDirs() error = %v, wantErr %v", err, tt.wantErr)
|
},
|
||||||
|
wantCode: 200,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tc := range testCases {
|
||||||
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
|
ret, err := cdnManager.RefreshDirs(tc.args.dirs)
|
||||||
|
if err != nil || ret.Code != tc.wantCode {
|
||||||
|
if ret.Error == "refresh dir limit error" {
|
||||||
|
t.Logf("RefreshDirs() error=%v", ret.Error)
|
||||||
|
} else {
|
||||||
|
t.Errorf("RefreshDirs() error = %v, %v", err, ret.Error)
|
||||||
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if !reflect.DeepEqual(gotResult, tt.wantResult) {
|
|
||||||
t.Errorf("RefreshDirs() = %v, want %v", gotResult, tt.wantResult)
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//TestPrefetchUrls
|
||||||
func TestPrefetchUrls(t *testing.T) {
|
func TestPrefetchUrls(t *testing.T) {
|
||||||
type args struct {
|
type args struct {
|
||||||
urls []string
|
urls []string
|
||||||
}
|
}
|
||||||
tests := []struct {
|
|
||||||
name string
|
testCases := []struct {
|
||||||
args args
|
name string
|
||||||
wantResult PrefetchResp
|
args args
|
||||||
wantErr bool
|
wantCode int
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
name: "refresh_test_1",
|
name: "CdnManager_PrefetchUrls",
|
||||||
args: args{
|
args: args{
|
||||||
urls: []string{testURL},
|
urls: testUrls,
|
||||||
},
|
},
|
||||||
wantErr: false,
|
wantCode: 200,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
kodo.SetMac(ak, sk)
|
|
||||||
for _, tt := range tests {
|
for _, tc := range testCases {
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
_, err := PrefetchUrls(tt.args.urls)
|
ret, err := cdnManager.PrefetchUrls(tc.args.urls)
|
||||||
if (err != nil) != tt.wantErr {
|
if err != nil || ret.Code != tc.wantCode {
|
||||||
t.Errorf("PrefetchUrls() error = %v, wantErr %v", err, tt.wantErr)
|
t.Errorf("PrefetchUrls() error = %v, %v", err, ret.Error)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func newBucket() (bucket kodo.Bucket) {
|
//TestGetCdnLogList
|
||||||
|
func TestGetCdnLogList(t *testing.T) {
|
||||||
ak := os.Getenv("QINIU_ACCESS_KEY")
|
type args struct {
|
||||||
sk := os.Getenv("QINIU_SECRET_KEY")
|
date string
|
||||||
if ak == "" || sk == "" {
|
domains []string
|
||||||
panic("require ACCESS_KEY & SECRET_KEY")
|
|
||||||
}
|
}
|
||||||
kodo.SetMac(ak, sk)
|
|
||||||
|
|
||||||
testBucketName = os.Getenv("QINIU_TEST_BUCKET")
|
testCases := []struct {
|
||||||
domain = os.Getenv("QINIU_TEST_DOMAIN")
|
name string
|
||||||
if testBucketName == "" || domain == "" {
|
args args
|
||||||
panic("require test env")
|
}{
|
||||||
|
{
|
||||||
|
name: "CdnManager_TestGetCdnLogList",
|
||||||
|
args: args{
|
||||||
|
date: logDate,
|
||||||
|
domains: []string{domain},
|
||||||
|
},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
client = kodo.NewWithoutZone(nil)
|
|
||||||
|
|
||||||
return client.Bucket(testBucketName)
|
for _, tc := range testCases {
|
||||||
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
|
_, err := cdnManager.GetCdnLogList(tc.args.date, tc.args.domains)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("GetCdnLogList() error = %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
26
vendor/github.com/qiniu/api.v7/cdn/doc.go
generated
vendored
26
vendor/github.com/qiniu/api.v7/cdn/doc.go
generated
vendored
@@ -1,25 +1,3 @@
|
|||||||
/*
|
// cdn 包提供了 Fusion CDN的常见功能。相关功能的文档参考:https://developer.qiniu.com/fusion。
|
||||||
包 github.com/qiniu/api.v7/cdn 提供了七牛CDN的API功能
|
// 目前提供了文件和目录刷新,文件预取,获取域名带宽和流量数据,获取域名日志列表等功能。
|
||||||
首先,我们要配置下 AccessKey/SecretKey,
|
|
||||||
import "github.com/qiniu/api.v7/kodo"
|
|
||||||
|
|
||||||
kodo.SetMac("ak", "sk")
|
|
||||||
设置了AccessKey/SecretKey 就可以使用cdn的各类功能
|
|
||||||
|
|
||||||
比如我们要生成一个带时间戳防盗链的链接:
|
|
||||||
q :=url.Values{}// url.Values 请求参数
|
|
||||||
link, err := cdn.CreateTimestampAntileechURL(""http://www.qiniu.com/abc/bcc/aa-s.mp4?x=2&y=3", "encryptedkey", 20)
|
|
||||||
if err != nil {
|
|
||||||
fmt.Println(err)
|
|
||||||
}
|
|
||||||
fmt.Println(link)
|
|
||||||
|
|
||||||
又或者我们要列出CDN日志及其下载地址:
|
|
||||||
resp, err := cdn.GetCdnLogList("2016-12-26", "x-mas.com")
|
|
||||||
if err != nil {
|
|
||||||
fmt.Println(err)
|
|
||||||
}
|
|
||||||
fmt.Println(resp)
|
|
||||||
|
|
||||||
*/
|
|
||||||
package cdn
|
package cdn
|
||||||
|
|||||||
92
vendor/github.com/qiniu/api.v7/cdn/logs.go
generated
vendored
92
vendor/github.com/qiniu/api.v7/cdn/logs.go
generated
vendored
@@ -1,92 +0,0 @@
|
|||||||
package cdn
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
|
||||||
"net/http"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/qiniu/api.v7/auth/qbox"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
LIST_LOG_API = "http://fusion.qiniuapi.com/v2/tune/log/list"
|
|
||||||
)
|
|
||||||
|
|
||||||
// ListLogRequest 日志下载请求内容
|
|
||||||
type ListLogRequest struct {
|
|
||||||
Day string `json:"day"`
|
|
||||||
Domains string `json:"domains"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// ListLogResult 日志下载相应内容
|
|
||||||
type ListLogResult struct {
|
|
||||||
Code int `json:"code"`
|
|
||||||
Error string `json:"error"`
|
|
||||||
Data map[string][]LogDomainInfo `json:"data"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// LogDomainInfo 日志下载信息
|
|
||||||
type LogDomainInfo struct {
|
|
||||||
Name string `json:"name"`
|
|
||||||
Size int64 `json:"size"`
|
|
||||||
ModifiedTime int64 `json:"mtime"`
|
|
||||||
URL string `json:"url"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetCdnLogList 获取CDN域名访问日志的下载链接
|
|
||||||
// http://developer.qiniu.com/article/fusion/api/log.html
|
|
||||||
func GetCdnLogList(date, domains string) (domainLogs []LogDomainInfo, err error) {
|
|
||||||
|
|
||||||
//new log query request
|
|
||||||
logReq := ListLogRequest{
|
|
||||||
Day: date,
|
|
||||||
Domains: domains,
|
|
||||||
}
|
|
||||||
|
|
||||||
logReqBytes, _ := json.Marshal(&logReq)
|
|
||||||
req, reqErr := http.NewRequest("POST", LIST_LOG_API, bytes.NewReader(logReqBytes))
|
|
||||||
if reqErr != nil {
|
|
||||||
err = fmt.Errorf("New request error, %s", reqErr)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
mac := qbox.NewMac("", "")
|
|
||||||
token, signErr := mac.SignRequest(req, false)
|
|
||||||
if signErr != nil {
|
|
||||||
err = signErr
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
req.Header.Add("Authorization", "QBox "+token)
|
|
||||||
req.Header.Add("Content-Type", "application/json")
|
|
||||||
|
|
||||||
resp, respErr := http.DefaultClient.Do(req)
|
|
||||||
if respErr != nil {
|
|
||||||
err = fmt.Errorf("Get response error, %s", respErr)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
defer resp.Body.Close()
|
|
||||||
|
|
||||||
listLogResult := ListLogResult{}
|
|
||||||
decoder := json.NewDecoder(resp.Body)
|
|
||||||
if decodeErr := decoder.Decode(&listLogResult); decodeErr != nil {
|
|
||||||
err = fmt.Errorf("Parse response error, %s", decodeErr)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if resp.StatusCode != http.StatusOK {
|
|
||||||
err = fmt.Errorf("Get log list error, %d %s", listLogResult.Code, listLogResult.Error)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
domainItems := strings.Split(domains, ";")
|
|
||||||
|
|
||||||
for _, domain := range domainItems {
|
|
||||||
for _, v := range listLogResult.Data[domain] {
|
|
||||||
domainLogs = append(domainLogs, v)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return
|
|
||||||
|
|
||||||
}
|
|
||||||
48
vendor/github.com/qiniu/api.v7/cdn/logs_test.go
generated
vendored
48
vendor/github.com/qiniu/api.v7/cdn/logs_test.go
generated
vendored
@@ -1,48 +0,0 @@
|
|||||||
package cdn
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"reflect"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/qiniu/api.v7/kodo"
|
|
||||||
)
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
kodo.SetMac(ak, sk)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestGetCdnLogList(t *testing.T) {
|
|
||||||
type args struct {
|
|
||||||
date string
|
|
||||||
domains string
|
|
||||||
}
|
|
||||||
tests := []struct {
|
|
||||||
name string
|
|
||||||
args args
|
|
||||||
wantDomainLogs []LogDomainInfo
|
|
||||||
wantErr bool
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
name: "getCdnLogListTest",
|
|
||||||
args: args{
|
|
||||||
date: testDate,
|
|
||||||
domains: domain,
|
|
||||||
},
|
|
||||||
wantErr: false,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
for _, tt := range tests {
|
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
|
||||||
gotDomainLogs, err := GetCdnLogList(tt.args.date, tt.args.domains)
|
|
||||||
if (err != nil) != tt.wantErr {
|
|
||||||
t.Errorf("GetCdnLogList() error = %v, wantErr %v", err, tt.wantErr)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
fmt.Println(domain, gotDomainLogs)
|
|
||||||
if !reflect.DeepEqual(gotDomainLogs, tt.wantDomainLogs) {
|
|
||||||
t.Errorf("GetCdnLogList() = %v, want %v", gotDomainLogs, tt.wantDomainLogs)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
10
vendor/github.com/qiniu/api.v7/conf/conf.go
generated
vendored
10
vendor/github.com/qiniu/api.v7/conf/conf.go
generated
vendored
@@ -9,19 +9,13 @@ import (
|
|||||||
"github.com/qiniu/x/rpc.v7"
|
"github.com/qiniu/x/rpc.v7"
|
||||||
)
|
)
|
||||||
|
|
||||||
var version = "7.1.0"
|
var version = "7.2.3"
|
||||||
|
|
||||||
var ACCESS_KEY string
|
|
||||||
var SECRET_KEY string
|
|
||||||
|
|
||||||
// ----------------------------------------------------------
|
|
||||||
|
|
||||||
const (
|
const (
|
||||||
ctypeAppName = ctype.ALPHA | ctype.DIGIT | ctype.UNDERLINE | ctype.SPACE_BAR | ctype.SUB | ctype.DOT
|
ctypeAppName = ctype.ALPHA | ctype.DIGIT | ctype.UNDERLINE | ctype.SPACE_BAR | ctype.SUB | ctype.DOT
|
||||||
)
|
)
|
||||||
|
|
||||||
// userApp should be [A-Za-z0-9_\ \-\.]*
|
// userApp should be [A-Za-z0-9_\ \-\.]*
|
||||||
//
|
|
||||||
func SetAppName(userApp string) error {
|
func SetAppName(userApp string) error {
|
||||||
if userApp != "" && !ctype.IsType(ctypeAppName, userApp) {
|
if userApp != "" && !ctype.IsType(ctypeAppName, userApp) {
|
||||||
return syscall.EINVAL
|
return syscall.EINVAL
|
||||||
@@ -34,5 +28,3 @@ func SetAppName(userApp string) error {
|
|||||||
func init() {
|
func init() {
|
||||||
SetAppName("")
|
SetAppName("")
|
||||||
}
|
}
|
||||||
|
|
||||||
// ----------------------------------------------------------
|
|
||||||
|
|||||||
2
vendor/github.com/qiniu/api.v7/conf/doc.go
generated
vendored
Normal file
2
vendor/github.com/qiniu/api.v7/conf/doc.go
generated
vendored
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
// conf 包提供了设置APP名称的方法。该APP名称会被放入API请求的UserAgent中,方便后续查询日志分析问题。
|
||||||
|
package conf
|
||||||
16
vendor/github.com/qiniu/api.v7/doc.go
generated
vendored
16
vendor/github.com/qiniu/api.v7/doc.go
generated
vendored
@@ -1,16 +1,13 @@
|
|||||||
/*
|
/*
|
||||||
包 github.com/qiniu/api.v7 是七牛 Go 语言 SDK v7.x 版本
|
|
||||||
|
|
||||||
七牛对象存储,我们取了一个好听的名字,叫 KODO Blob Storage。要使用它,你主要和以下两个包打交道:
|
包 github.com/qiniu/api.v7 是七牛 Go 语言 SDK v7.x 版本。
|
||||||
|
|
||||||
import "github.com/qiniu/api.v7/kodo"
|
主要提供了存储的数据上传,下载,管理以及CDN相关的功能。要求Go语言版本>=1.7.0。
|
||||||
import "github.com/qiniu/api.v7/kodocli"
|
|
||||||
|
|
||||||
如果您是在业务服务器(服务器端)调用七牛云存储的服务,请使用 github.com/qiniu/api.v7/kodo。
|
Go SDK 中主要包含几个包:
|
||||||
|
|
||||||
|
auth 包提供鉴权相关方法,conf 包提供配置相关方法,cdn包提供CDN相关的功能,storage包提供存储相关的功能。
|
||||||
|
|
||||||
如果您是在客户端(比如:Android/iOS 设备、Windows/Mac/Linux 桌面环境)调用七牛云存储的服务,请使用 github.com/qiniu/api.v7/kodocli。
|
|
||||||
注意,在这种场合下您不应该在任何地方配置 AccessKey/SecretKey。泄露 AccessKey/SecretKey 如同泄露您的用户名/密码一样十分危险,
|
|
||||||
会影响您的数据安全。
|
|
||||||
*/
|
*/
|
||||||
package api
|
package api
|
||||||
|
|
||||||
@@ -18,6 +15,5 @@ import (
|
|||||||
_ "github.com/qiniu/api.v7/auth/qbox"
|
_ "github.com/qiniu/api.v7/auth/qbox"
|
||||||
_ "github.com/qiniu/api.v7/cdn"
|
_ "github.com/qiniu/api.v7/cdn"
|
||||||
_ "github.com/qiniu/api.v7/conf"
|
_ "github.com/qiniu/api.v7/conf"
|
||||||
_ "github.com/qiniu/api.v7/kodo"
|
_ "github.com/qiniu/api.v7/storage"
|
||||||
_ "github.com/qiniu/api.v7/kodocli"
|
|
||||||
)
|
)
|
||||||
|
|||||||
35
vendor/github.com/qiniu/api.v7/examples/bucket_image_unimage.go
generated
vendored
Normal file
35
vendor/github.com/qiniu/api.v7/examples/bucket_image_unimage.go
generated
vendored
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"github.com/qiniu/api.v7/auth/qbox"
|
||||||
|
"github.com/qiniu/api.v7/storage"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
accessKey = os.Getenv("QINIU_ACCESS_KEY")
|
||||||
|
secretKey = os.Getenv("QINIU_SECRET_KEY")
|
||||||
|
bucket = os.Getenv("QINIU_TEST_BUCKET")
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
cfg := storage.Config{}
|
||||||
|
mac := qbox.NewMac(accessKey, secretKey)
|
||||||
|
bucketManger := storage.NewBucketManager(mac, &cfg)
|
||||||
|
siteURL := "http://devtools.qiniu.com"
|
||||||
|
|
||||||
|
// 设置镜像存储
|
||||||
|
err := bucketManger.SetImage(siteURL, bucket)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 取消设置镜像存储
|
||||||
|
err = bucketManger.UnsetImage(bucket)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
20
vendor/github.com/qiniu/api.v7/examples/cdn_create_timestamp_antileech_url.go
generated
vendored
Normal file
20
vendor/github.com/qiniu/api.v7/examples/cdn_create_timestamp_antileech_url.go
generated
vendored
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/qiniu/api.v7/cdn"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
urlStr := "http://image.example.com/qiniu_do_not_delete.gif"
|
||||||
|
cryptKey := "your crypt key"
|
||||||
|
deadline := time.Now().Add(time.Second * 3600).Unix()
|
||||||
|
accessUrl, err := cdn.CreateTimestampAntileechURL(urlStr, cryptKey, deadline)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
fmt.Println(accessUrl)
|
||||||
|
}
|
||||||
31
vendor/github.com/qiniu/api.v7/examples/cdn_get_bandwidth_data.go
generated
vendored
Normal file
31
vendor/github.com/qiniu/api.v7/examples/cdn_get_bandwidth_data.go
generated
vendored
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"github.com/qiniu/api.v7/auth/qbox"
|
||||||
|
"github.com/qiniu/api.v7/cdn"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
accessKey = os.Getenv("QINIU_ACCESS_KEY")
|
||||||
|
secretKey = os.Getenv("QINIU_SECRET_KEY")
|
||||||
|
domain = os.Getenv("QINIU_TEST_DOMAIN")
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
mac := qbox.NewMac(accessKey, secretKey)
|
||||||
|
cdnManager := cdn.NewCdnManager(mac)
|
||||||
|
|
||||||
|
startDate := "2017-07-20"
|
||||||
|
endDate := "2017-07-30"
|
||||||
|
g := "day"
|
||||||
|
data, err := cdnManager.GetBandwidthData(startDate, endDate, g, []string{domain})
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Printf("%v\n", data)
|
||||||
|
}
|
||||||
31
vendor/github.com/qiniu/api.v7/examples/cdn_get_flux_data.go
generated
vendored
Normal file
31
vendor/github.com/qiniu/api.v7/examples/cdn_get_flux_data.go
generated
vendored
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"github.com/qiniu/api.v7/auth/qbox"
|
||||||
|
"github.com/qiniu/api.v7/cdn"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
accessKey = os.Getenv("QINIU_ACCESS_KEY")
|
||||||
|
secretKey = os.Getenv("QINIU_SECRET_KEY")
|
||||||
|
domain = os.Getenv("QINIU_TEST_DOMAIN")
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
mac := qbox.NewMac(accessKey, secretKey)
|
||||||
|
cdnManager := cdn.NewCdnManager(mac)
|
||||||
|
|
||||||
|
startDate := "2017-07-20"
|
||||||
|
endDate := "2017-07-30"
|
||||||
|
g := "day"
|
||||||
|
data, err := cdnManager.GetFluxData(startDate, endDate, g, []string{domain})
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Printf("%v\n", data)
|
||||||
|
}
|
||||||
37
vendor/github.com/qiniu/api.v7/examples/cdn_get_log_list.go
generated
vendored
Normal file
37
vendor/github.com/qiniu/api.v7/examples/cdn_get_log_list.go
generated
vendored
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"github.com/qiniu/api.v7/auth/qbox"
|
||||||
|
"github.com/qiniu/api.v7/cdn"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
accessKey = os.Getenv("QINIU_ACCESS_KEY")
|
||||||
|
secretKey = os.Getenv("QINIU_SECRET_KEY")
|
||||||
|
domain = os.Getenv("QINIU_TEST_DOMAIN")
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
mac := qbox.NewMac(accessKey, secretKey)
|
||||||
|
cdnManager := cdn.NewCdnManager(mac)
|
||||||
|
|
||||||
|
domains := []string{
|
||||||
|
domain,
|
||||||
|
}
|
||||||
|
day := "2017-07-30"
|
||||||
|
ret, err := cdnManager.GetCdnLogList(day, domains)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
domainLogs := ret.Data
|
||||||
|
for domain, logs := range domainLogs {
|
||||||
|
fmt.Println(domain)
|
||||||
|
for _, item := range logs {
|
||||||
|
fmt.Println(item.Name, item.URL, item.Size, item.ModifiedTime)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
33
vendor/github.com/qiniu/api.v7/examples/cdn_prefetch_urls.go
generated
vendored
Normal file
33
vendor/github.com/qiniu/api.v7/examples/cdn_prefetch_urls.go
generated
vendored
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"github.com/qiniu/api.v7/auth/qbox"
|
||||||
|
"github.com/qiniu/api.v7/cdn"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
accessKey = os.Getenv("QINIU_ACCESS_KEY")
|
||||||
|
secretKey = os.Getenv("QINIU_SECRET_KEY")
|
||||||
|
domain = os.Getenv("QINIU_TEST_DOMAIN")
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
mac := qbox.NewMac(accessKey, secretKey)
|
||||||
|
cdnManager := cdn.NewCdnManager(mac)
|
||||||
|
|
||||||
|
// 预取链接,单次请求链接不可以超过100个,如果超过,请分批发送请求
|
||||||
|
urlsToPrefetch := []string{
|
||||||
|
"http://if-pbl.qiniudn.com/qiniu.png",
|
||||||
|
"http://if-pbl.qiniudn.com/github.png",
|
||||||
|
}
|
||||||
|
ret, err := cdnManager.PrefetchUrls(urlsToPrefetch)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
fmt.Println(ret.Code)
|
||||||
|
fmt.Println(ret.RequestID)
|
||||||
|
}
|
||||||
48
vendor/github.com/qiniu/api.v7/examples/cdn_refresh_urls_and_dirs.go
generated
vendored
Normal file
48
vendor/github.com/qiniu/api.v7/examples/cdn_refresh_urls_and_dirs.go
generated
vendored
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"github.com/qiniu/api.v7/auth/qbox"
|
||||||
|
"github.com/qiniu/api.v7/cdn"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
accessKey = os.Getenv("QINIU_ACCESS_KEY")
|
||||||
|
secretKey = os.Getenv("QINIU_SECRET_KEY")
|
||||||
|
domain = os.Getenv("QINIU_TEST_DOMAIN")
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
mac := qbox.NewMac(accessKey, secretKey)
|
||||||
|
cdnManager := cdn.NewCdnManager(mac)
|
||||||
|
|
||||||
|
//刷新链接,单次请求链接不可以超过100个,如果超过,请分批发送请求
|
||||||
|
urlsToRefresh := []string{
|
||||||
|
"http://if-pbl.qiniudn.com/qiniu.png",
|
||||||
|
"http://if-pbl.qiniudn.com/github.png",
|
||||||
|
}
|
||||||
|
ret, err := cdnManager.RefreshUrls(urlsToRefresh)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
fmt.Println(ret.Code)
|
||||||
|
fmt.Println(ret.RequestID)
|
||||||
|
|
||||||
|
// 刷新目录,刷新目录需要联系七牛技术支持开通权限
|
||||||
|
// 单次请求链接不可以超过10个,如果超过,请分批发送请求
|
||||||
|
dirsToRefresh := []string{
|
||||||
|
"http://if-pbl.qiniudn.com/images/",
|
||||||
|
"http://if-pbl.qiniudn.com/static/",
|
||||||
|
}
|
||||||
|
ret, err = cdnManager.RefreshDirs(dirsToRefresh)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
fmt.Println(ret.Code)
|
||||||
|
fmt.Println(ret.RequestID)
|
||||||
|
fmt.Println(ret.Error)
|
||||||
|
}
|
||||||
91
vendor/github.com/qiniu/api.v7/examples/create_uptoken.go
generated
vendored
Normal file
91
vendor/github.com/qiniu/api.v7/examples/create_uptoken.go
generated
vendored
Normal file
@@ -0,0 +1,91 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/base64"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/qiniu/api.v7/auth/qbox"
|
||||||
|
"github.com/qiniu/api.v7/storage"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
accessKey = os.Getenv("QINIU_ACCESS_KEY")
|
||||||
|
secretKey = os.Getenv("QINIU_SECRET_KEY")
|
||||||
|
bucket = os.Getenv("QINIU_TEST_BUCKET")
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
|
||||||
|
// 简单上传凭证
|
||||||
|
putPolicy := storage.PutPolicy{
|
||||||
|
Scope: bucket,
|
||||||
|
}
|
||||||
|
mac := qbox.NewMac(accessKey, secretKey)
|
||||||
|
upToken := putPolicy.UploadToken(mac)
|
||||||
|
fmt.Println(upToken)
|
||||||
|
|
||||||
|
// 设置上传凭证有效期
|
||||||
|
putPolicy = storage.PutPolicy{
|
||||||
|
Scope: bucket,
|
||||||
|
}
|
||||||
|
putPolicy.Expires = 7200 //示例2小时有效期
|
||||||
|
|
||||||
|
upToken = putPolicy.UploadToken(mac)
|
||||||
|
fmt.Println(upToken)
|
||||||
|
|
||||||
|
// 覆盖上传凭证
|
||||||
|
// 需要覆盖的文件名
|
||||||
|
keyToOverwrite := "qiniu.mp4"
|
||||||
|
putPolicy = storage.PutPolicy{
|
||||||
|
Scope: fmt.Sprintf("%s:%s", bucket, keyToOverwrite),
|
||||||
|
}
|
||||||
|
upToken = putPolicy.UploadToken(mac)
|
||||||
|
fmt.Println(upToken)
|
||||||
|
|
||||||
|
// 自定义上传回复凭证
|
||||||
|
putPolicy = storage.PutPolicy{
|
||||||
|
Scope: bucket,
|
||||||
|
ReturnBody: `{"key":"$(key)","hash":"$(etag)","fsize":$(fsize),"bucket":"$(bucket)","name":"$(x:name)"}`,
|
||||||
|
}
|
||||||
|
upToken = putPolicy.UploadToken(mac)
|
||||||
|
fmt.Println(upToken)
|
||||||
|
|
||||||
|
// 带回调业务服务器的凭证(JSON方式)
|
||||||
|
putPolicy = storage.PutPolicy{
|
||||||
|
Scope: bucket,
|
||||||
|
CallbackURL: "http://api.example.com/qiniu/upload/callback",
|
||||||
|
CallbackBody: `{"key":"$(key)","hash":"$(etag)","fsize":$(fsize),"bucket":"$(bucket)","name":"$(x:name)"}`,
|
||||||
|
CallbackBodyType: "application/json",
|
||||||
|
}
|
||||||
|
upToken = putPolicy.UploadToken(mac)
|
||||||
|
fmt.Println(upToken)
|
||||||
|
|
||||||
|
// 带回调业务服务器的凭证(URL方式)
|
||||||
|
putPolicy = storage.PutPolicy{
|
||||||
|
Scope: bucket,
|
||||||
|
CallbackURL: "http://api.example.com/qiniu/upload/callback",
|
||||||
|
CallbackBody: "key=$(key)&hash=$(etag)&bucket=$(bucket)&fsize=$(fsize)&name=$(x:name)",
|
||||||
|
}
|
||||||
|
upToken = putPolicy.UploadToken(mac)
|
||||||
|
fmt.Println(upToken)
|
||||||
|
|
||||||
|
// 带数据处理的凭证
|
||||||
|
saveMp4Entry := base64.URLEncoding.EncodeToString([]byte(bucket + ":avthumb_test_target.mp4"))
|
||||||
|
saveJpgEntry := base64.URLEncoding.EncodeToString([]byte(bucket + ":vframe_test_target.jpg"))
|
||||||
|
//数据处理指令,支持多个指令
|
||||||
|
avthumbMp4Fop := "avthumb/mp4|saveas/" + saveMp4Entry
|
||||||
|
vframeJpgFop := "vframe/jpg/offset/1|saveas/" + saveJpgEntry
|
||||||
|
//连接多个操作指令
|
||||||
|
persistentOps := strings.Join([]string{avthumbMp4Fop, vframeJpgFop}, ";")
|
||||||
|
pipeline := "test"
|
||||||
|
putPolicy = storage.PutPolicy{
|
||||||
|
Scope: bucket,
|
||||||
|
PersistentOps: persistentOps,
|
||||||
|
PersistentPipeline: pipeline,
|
||||||
|
PersistentNotifyURL: "http://api.example.com/qiniu/pfop/notify",
|
||||||
|
}
|
||||||
|
upToken = putPolicy.UploadToken(mac)
|
||||||
|
fmt.Println(upToken)
|
||||||
|
}
|
||||||
76
vendor/github.com/qiniu/api.v7/examples/form_upload_simple.go
generated
vendored
Normal file
76
vendor/github.com/qiniu/api.v7/examples/form_upload_simple.go
generated
vendored
Normal file
@@ -0,0 +1,76 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"net"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"net/http"
|
||||||
|
"net/url"
|
||||||
|
|
||||||
|
"github.com/qiniu/api.v7/auth/qbox"
|
||||||
|
"github.com/qiniu/api.v7/storage"
|
||||||
|
"github.com/qiniu/x/rpc.v7"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
accessKey = os.Getenv("QINIU_ACCESS_KEY")
|
||||||
|
secretKey = os.Getenv("QINIU_SECRET_KEY")
|
||||||
|
bucket = os.Getenv("QINIU_TEST_BUCKET")
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
localFile := "/Users/jemy/Documents/github.png"
|
||||||
|
key := "github-x.png"
|
||||||
|
putPolicy := storage.PutPolicy{
|
||||||
|
Scope: bucket + ":" + key,
|
||||||
|
}
|
||||||
|
|
||||||
|
mac := qbox.NewMac(accessKey, secretKey)
|
||||||
|
upToken := putPolicy.UploadToken(mac)
|
||||||
|
cfg := storage.Config{}
|
||||||
|
// 空间对应的机房
|
||||||
|
cfg.Zone = &storage.ZoneHuadong
|
||||||
|
// 是否使用https域名
|
||||||
|
cfg.UseHTTPS = false
|
||||||
|
// 上传是否使用CDN上传加速
|
||||||
|
cfg.UseCdnDomains = false
|
||||||
|
|
||||||
|
//设置代理
|
||||||
|
proxyURL := "http://localhost:8888"
|
||||||
|
proxyURI, _ := url.Parse(proxyURL)
|
||||||
|
|
||||||
|
//绑定网卡
|
||||||
|
nicIP := "100.100.33.138"
|
||||||
|
dialer := &net.Dialer{
|
||||||
|
LocalAddr: &net.TCPAddr{
|
||||||
|
IP: net.ParseIP(nicIP),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
//构建代理client对象
|
||||||
|
client := http.Client{
|
||||||
|
Transport: &http.Transport{
|
||||||
|
Proxy: http.ProxyURL(proxyURI),
|
||||||
|
Dial: dialer.Dial,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
// 构建表单上传的对象
|
||||||
|
formUploader := storage.NewFormUploaderEx(&cfg, &rpc.Client{Client: &client})
|
||||||
|
ret := storage.PutRet{}
|
||||||
|
// 可选配置
|
||||||
|
putExtra := storage.PutExtra{
|
||||||
|
Params: map[string]string{
|
||||||
|
"x:name": "github logo",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
//putExtra.NoCrc32Check = true
|
||||||
|
err := formUploader.PutFile(context.Background(), &ret, upToken, key, localFile, &putExtra)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
fmt.Println(ret.Key, ret.Hash)
|
||||||
|
}
|
||||||
32
vendor/github.com/qiniu/api.v7/examples/prefop.go
generated
vendored
Normal file
32
vendor/github.com/qiniu/api.v7/examples/prefop.go
generated
vendored
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"github.com/qiniu/api.v7/auth/qbox"
|
||||||
|
"github.com/qiniu/api.v7/storage"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
accessKey = os.Getenv("QINIU_ACCESS_KEY")
|
||||||
|
secretKey = os.Getenv("QINIU_SECRET_KEY")
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
mac := qbox.NewMac(accessKey, secretKey)
|
||||||
|
cfg := storage.Config{
|
||||||
|
UseHTTPS: false,
|
||||||
|
}
|
||||||
|
// 指定空间所在的区域,如果不指定将自动探测
|
||||||
|
// 如果没有特殊需求,默认不需要指定
|
||||||
|
//cfg.Zone=&storage.ZoneHuabei
|
||||||
|
operationManager := storage.NewOperationManager(mac, &cfg)
|
||||||
|
persistentId := "z0.597f28b445a2650c994bb208"
|
||||||
|
ret, err := operationManager.Prefop(persistentId)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
fmt.Println(ret.String())
|
||||||
|
}
|
||||||
127
vendor/github.com/qiniu/api.v7/examples/resume_upload_advanced.go
generated
vendored
Normal file
127
vendor/github.com/qiniu/api.v7/examples/resume_upload_advanced.go
generated
vendored
Normal file
@@ -0,0 +1,127 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/md5"
|
||||||
|
"encoding/hex"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"context"
|
||||||
|
"github.com/qiniu/api.v7/auth/qbox"
|
||||||
|
"github.com/qiniu/api.v7/storage"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
accessKey = os.Getenv("QINIU_ACCESS_KEY")
|
||||||
|
secretKey = os.Getenv("QINIU_SECRET_KEY")
|
||||||
|
bucket = os.Getenv("QINIU_TEST_BUCKET")
|
||||||
|
)
|
||||||
|
|
||||||
|
func md5Hex(str string) string {
|
||||||
|
h := md5.New()
|
||||||
|
h.Write([]byte(str))
|
||||||
|
return hex.EncodeToString(h.Sum(nil))
|
||||||
|
}
|
||||||
|
|
||||||
|
type ProgressRecord struct {
|
||||||
|
Progresses []storage.BlkputRet `json:"progresses"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
|
||||||
|
localFile := "your local file path"
|
||||||
|
key := "your file save key"
|
||||||
|
|
||||||
|
putPolicy := storage.PutPolicy{
|
||||||
|
Scope: bucket,
|
||||||
|
}
|
||||||
|
mac := qbox.NewMac(accessKey, secretKey)
|
||||||
|
upToken := putPolicy.UploadToken(mac)
|
||||||
|
|
||||||
|
cfg := storage.Config{}
|
||||||
|
// 空间对应的机房
|
||||||
|
cfg.Zone = &storage.ZoneHuadong
|
||||||
|
// 是否使用https域名
|
||||||
|
cfg.UseHTTPS = false
|
||||||
|
// 上传是否使用CDN上传加速
|
||||||
|
cfg.UseCdnDomains = false
|
||||||
|
|
||||||
|
// 必须仔细选择一个能标志上传唯一性的 recordKey 用来记录上传进度
|
||||||
|
// 我们这里采用 md5(bucket+key+local_path+local_file_last_modified)+".progress" 作为记录上传进度的文件名
|
||||||
|
fileInfo, statErr := os.Stat(localFile)
|
||||||
|
if statErr != nil {
|
||||||
|
fmt.Println(statErr)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
fileSize := fileInfo.Size()
|
||||||
|
fileLmd := fileInfo.ModTime().UnixNano()
|
||||||
|
recordKey := md5Hex(fmt.Sprintf("%s:%s:%s:%s", bucket, key, localFile, fileLmd)) + ".progress"
|
||||||
|
|
||||||
|
// 指定的进度文件保存目录,实际情况下,请确保该目录存在,而且只用于记录进度文件
|
||||||
|
recordDir := "/Users/jemy/Temp/progress"
|
||||||
|
mErr := os.MkdirAll(recordDir, 0755)
|
||||||
|
if mErr != nil {
|
||||||
|
fmt.Println("mkdir for record dir error,", mErr)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
recordPath := filepath.Join(recordDir, recordKey)
|
||||||
|
|
||||||
|
progressRecord := ProgressRecord{}
|
||||||
|
// 尝试从旧的进度文件中读取进度
|
||||||
|
recordFp, openErr := os.Open(recordPath)
|
||||||
|
if openErr == nil {
|
||||||
|
progressBytes, readErr := ioutil.ReadAll(recordFp)
|
||||||
|
if readErr == nil {
|
||||||
|
mErr := json.Unmarshal(progressBytes, &progressRecord)
|
||||||
|
if mErr == nil {
|
||||||
|
// 检查context 是否过期,避免701错误
|
||||||
|
for _, item := range progressRecord.Progresses {
|
||||||
|
if storage.IsContextExpired(item) {
|
||||||
|
fmt.Println(item.ExpiredAt)
|
||||||
|
progressRecord.Progresses = make([]storage.BlkputRet, storage.BlockCount(fileSize))
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
recordFp.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(progressRecord.Progresses) == 0 {
|
||||||
|
progressRecord.Progresses = make([]storage.BlkputRet, storage.BlockCount(fileSize))
|
||||||
|
}
|
||||||
|
|
||||||
|
resumeUploader := storage.NewResumeUploader(&cfg)
|
||||||
|
ret := storage.PutRet{}
|
||||||
|
progressLock := sync.RWMutex{}
|
||||||
|
|
||||||
|
putExtra := storage.RputExtra{
|
||||||
|
Progresses: progressRecord.Progresses,
|
||||||
|
Notify: func(blkIdx int, blkSize int, ret *storage.BlkputRet) {
|
||||||
|
progressLock.Lock()
|
||||||
|
defer progressLock.Unlock()
|
||||||
|
//将进度序列化,然后写入文件
|
||||||
|
progressRecord.Progresses[blkIdx] = *ret
|
||||||
|
progressBytes, _ := json.Marshal(progressRecord)
|
||||||
|
fmt.Println("write progress file", blkIdx, recordPath)
|
||||||
|
wErr := ioutil.WriteFile(recordPath, progressBytes, 0644)
|
||||||
|
if wErr != nil {
|
||||||
|
fmt.Println("write progress file error,", wErr)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
err := resumeUploader.PutFile(context.Background(), &ret, upToken, key, localFile, &putExtra)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
//上传成功之后,一定记得删除这个进度文件
|
||||||
|
os.Remove(recordPath)
|
||||||
|
fmt.Println(ret.Key, ret.Hash)
|
||||||
|
}
|
||||||
72
vendor/github.com/qiniu/api.v7/examples/resume_upload_simple.go
generated
vendored
Normal file
72
vendor/github.com/qiniu/api.v7/examples/resume_upload_simple.go
generated
vendored
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"net"
|
||||||
|
"net/http"
|
||||||
|
"net/url"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"github.com/qiniu/api.v7/auth/qbox"
|
||||||
|
"github.com/qiniu/api.v7/storage"
|
||||||
|
"github.com/qiniu/x/rpc.v7"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
accessKey = os.Getenv("QINIU_ACCESS_KEY")
|
||||||
|
secretKey = os.Getenv("QINIU_SECRET_KEY")
|
||||||
|
bucket = os.Getenv("QINIU_TEST_BUCKET")
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
|
||||||
|
localFile := "/Users/jemy/Documents/github.png"
|
||||||
|
key := "qiniu-x.png"
|
||||||
|
|
||||||
|
putPolicy := storage.PutPolicy{
|
||||||
|
Scope: bucket,
|
||||||
|
}
|
||||||
|
mac := qbox.NewMac(accessKey, secretKey)
|
||||||
|
|
||||||
|
cfg := storage.Config{}
|
||||||
|
// 空间对应的机房
|
||||||
|
cfg.Zone = &storage.ZoneHuadong
|
||||||
|
// 是否使用https域名
|
||||||
|
cfg.UseHTTPS = false
|
||||||
|
// 上传是否使用CDN上传加速
|
||||||
|
cfg.UseCdnDomains = false
|
||||||
|
|
||||||
|
//设置代理
|
||||||
|
proxyURL := "http://localhost:8888"
|
||||||
|
proxyURI, _ := url.Parse(proxyURL)
|
||||||
|
|
||||||
|
//绑定网卡
|
||||||
|
nicIP := "100.100.33.138"
|
||||||
|
dialer := &net.Dialer{
|
||||||
|
LocalAddr: &net.TCPAddr{
|
||||||
|
IP: net.ParseIP(nicIP),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
//构建代理client对象
|
||||||
|
client := http.Client{
|
||||||
|
Transport: &http.Transport{
|
||||||
|
Proxy: http.ProxyURL(proxyURI),
|
||||||
|
Dial: dialer.Dial,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
resumeUploader := storage.NewResumeUploaderEx(&cfg, &rpc.Client{Client: &client})
|
||||||
|
upToken := putPolicy.UploadToken(mac)
|
||||||
|
|
||||||
|
ret := storage.PutRet{}
|
||||||
|
|
||||||
|
err := resumeUploader.PutFile(context.Background(), &ret, upToken, key, localFile, nil)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Println(ret.Key, ret.Hash)
|
||||||
|
}
|
||||||
64
vendor/github.com/qiniu/api.v7/examples/rs_batch_change_mime.go
generated
vendored
Normal file
64
vendor/github.com/qiniu/api.v7/examples/rs_batch_change_mime.go
generated
vendored
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"github.com/qiniu/api.v7/auth/qbox"
|
||||||
|
"github.com/qiniu/api.v7/storage"
|
||||||
|
"github.com/qiniu/x/rpc.v7"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
accessKey = os.Getenv("QINIU_ACCESS_KEY")
|
||||||
|
secretKey = os.Getenv("QINIU_SECRET_KEY")
|
||||||
|
bucket = os.Getenv("QINIU_TEST_BUCKET")
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
mac := qbox.NewMac(accessKey, secretKey)
|
||||||
|
|
||||||
|
cfg := storage.Config{
|
||||||
|
// 是否使用https域名进行资源管理
|
||||||
|
UseHTTPS: false,
|
||||||
|
}
|
||||||
|
// 指定空间所在的区域,如果不指定将自动探测
|
||||||
|
// 如果没有特殊需求,默认不需要指定
|
||||||
|
//cfg.Zone=&storage.ZoneHuabei
|
||||||
|
bucketManager := storage.NewBucketManager(mac, &cfg)
|
||||||
|
chgmKeys := map[string]string{
|
||||||
|
"github1.png": "image/x-png",
|
||||||
|
"github2.png": "image/x-png",
|
||||||
|
"github3.png": "image/x-png",
|
||||||
|
"github4.png": "image/x-png",
|
||||||
|
"github5.png": "image/x-png",
|
||||||
|
}
|
||||||
|
chgmOps := make([]string, 0, len(chgmKeys))
|
||||||
|
for key, newMime := range chgmKeys {
|
||||||
|
chgmOps = append(chgmOps, storage.URIChangeMime(bucket, key, newMime))
|
||||||
|
}
|
||||||
|
rets, err := bucketManager.Batch(chgmOps)
|
||||||
|
if err != nil {
|
||||||
|
// 遇到错误
|
||||||
|
if _, ok := err.(*rpc.ErrorInfo); ok {
|
||||||
|
for _, ret := range rets {
|
||||||
|
// 200 为成功
|
||||||
|
fmt.Printf("%d\n", ret.Code)
|
||||||
|
if ret.Code != 200 {
|
||||||
|
fmt.Printf("%s\n", ret.Data.Error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
fmt.Printf("batch error, %s", err)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// 完全成功
|
||||||
|
for _, ret := range rets {
|
||||||
|
// 200 为成功
|
||||||
|
fmt.Printf("%d\n", ret.Code)
|
||||||
|
if ret.Code != 200 {
|
||||||
|
fmt.Printf("%s\n", ret.Data.Error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
67
vendor/github.com/qiniu/api.v7/examples/rs_batch_change_type.go
generated
vendored
Normal file
67
vendor/github.com/qiniu/api.v7/examples/rs_batch_change_type.go
generated
vendored
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"github.com/qiniu/api.v7/auth/qbox"
|
||||||
|
"github.com/qiniu/api.v7/storage"
|
||||||
|
"github.com/qiniu/x/rpc.v7"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
accessKey = os.Getenv("QINIU_ACCESS_KEY")
|
||||||
|
secretKey = os.Getenv("QINIU_SECRET_KEY")
|
||||||
|
bucket = os.Getenv("QINIU_TEST_BUCKET")
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
mac := qbox.NewMac(accessKey, secretKey)
|
||||||
|
|
||||||
|
cfg := storage.Config{
|
||||||
|
// 是否使用https域名进行资源管理
|
||||||
|
UseHTTPS: false,
|
||||||
|
}
|
||||||
|
// 指定空间所在的区域,如果不指定将自动探测
|
||||||
|
// 如果没有特殊需求,默认不需要指定
|
||||||
|
//cfg.Zone=&storage.ZoneHuabei
|
||||||
|
bucketManager := storage.NewBucketManager(mac, &cfg)
|
||||||
|
|
||||||
|
//每个batch的操作数量不可以超过1000个,如果总数量超过1000,需要分批发送
|
||||||
|
|
||||||
|
chtypeKeys := map[string]int{
|
||||||
|
"github1.png": 1,
|
||||||
|
"github2.png": 1,
|
||||||
|
"github3.png": 1,
|
||||||
|
"github4.png": 1,
|
||||||
|
"github5.png": 1,
|
||||||
|
}
|
||||||
|
chtypeOps := make([]string, 0, len(chtypeKeys))
|
||||||
|
for key, fileType := range chtypeKeys {
|
||||||
|
chtypeOps = append(chtypeOps, storage.URIChangeType(bucket, key, fileType))
|
||||||
|
}
|
||||||
|
rets, err := bucketManager.Batch(chtypeOps)
|
||||||
|
if err != nil {
|
||||||
|
// 遇到错误
|
||||||
|
if _, ok := err.(*rpc.ErrorInfo); ok {
|
||||||
|
for _, ret := range rets {
|
||||||
|
// 200 为成功
|
||||||
|
fmt.Printf("%d\n", ret.Code)
|
||||||
|
if ret.Code != 200 {
|
||||||
|
fmt.Printf("%s\n", ret.Data.Error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
fmt.Printf("batch error, %s", err)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// 完全成功
|
||||||
|
for _, ret := range rets {
|
||||||
|
// 200 为成功
|
||||||
|
fmt.Printf("%d\n", ret.Code)
|
||||||
|
if ret.Code != 200 {
|
||||||
|
fmt.Printf("%s\n", ret.Data.Error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
67
vendor/github.com/qiniu/api.v7/examples/rs_batch_copy.go
generated
vendored
Normal file
67
vendor/github.com/qiniu/api.v7/examples/rs_batch_copy.go
generated
vendored
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"github.com/qiniu/api.v7/auth/qbox"
|
||||||
|
"github.com/qiniu/api.v7/storage"
|
||||||
|
"github.com/qiniu/x/rpc.v7"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
accessKey = os.Getenv("QINIU_ACCESS_KEY")
|
||||||
|
secretKey = os.Getenv("QINIU_SECRET_KEY")
|
||||||
|
bucket = os.Getenv("QINIU_TEST_BUCKET")
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
mac := qbox.NewMac(accessKey, secretKey)
|
||||||
|
|
||||||
|
cfg := storage.Config{
|
||||||
|
// 是否使用https域名进行资源管理
|
||||||
|
UseHTTPS: false,
|
||||||
|
}
|
||||||
|
// 指定空间所在的区域,如果不指定将自动探测
|
||||||
|
// 如果没有特殊需求,默认不需要指定
|
||||||
|
//cfg.Zone=&storage.ZoneHuabei
|
||||||
|
bucketManager := storage.NewBucketManager(mac, &cfg)
|
||||||
|
|
||||||
|
//每个batch的操作数量不可以超过1000个,如果总数量超过1000,需要分批发送
|
||||||
|
srcBucket := bucket
|
||||||
|
destBucket := bucket
|
||||||
|
force := true
|
||||||
|
copyKeys := map[string]string{
|
||||||
|
"github1.png": "github1-copy.png",
|
||||||
|
"github2.png": "github2-copy.png",
|
||||||
|
"github3.png": "github3-copy.png",
|
||||||
|
"github4.png": "github4-copy.png",
|
||||||
|
"github5.png": "github5-copy.png",
|
||||||
|
}
|
||||||
|
copyOps := make([]string, 0, len(copyKeys))
|
||||||
|
for srcKey, destKey := range copyKeys {
|
||||||
|
copyOps = append(copyOps, storage.URICopy(srcBucket, srcKey, destBucket, destKey, force))
|
||||||
|
}
|
||||||
|
rets, err := bucketManager.Batch(copyOps)
|
||||||
|
if err != nil {
|
||||||
|
// 遇到错误
|
||||||
|
if _, ok := err.(*rpc.ErrorInfo); ok {
|
||||||
|
for _, ret := range rets {
|
||||||
|
// 200 为成功
|
||||||
|
fmt.Printf("%d\n", ret.Code)
|
||||||
|
if ret.Code != 200 {
|
||||||
|
fmt.Printf("%s\n", ret.Data.Error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
fmt.Printf("batch error, %s", err)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// 完全成功
|
||||||
|
for _, ret := range rets {
|
||||||
|
// 200 为成功
|
||||||
|
fmt.Printf("%d\n", ret.Code)
|
||||||
|
fmt.Printf("%v\n", ret.Data)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
63
vendor/github.com/qiniu/api.v7/examples/rs_batch_delete.go
generated
vendored
Normal file
63
vendor/github.com/qiniu/api.v7/examples/rs_batch_delete.go
generated
vendored
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"github.com/qiniu/api.v7/auth/qbox"
|
||||||
|
"github.com/qiniu/api.v7/storage"
|
||||||
|
"github.com/qiniu/x/rpc.v7"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
accessKey = os.Getenv("QINIU_ACCESS_KEY")
|
||||||
|
secretKey = os.Getenv("QINIU_SECRET_KEY")
|
||||||
|
bucket = os.Getenv("QINIU_TEST_BUCKET")
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
mac := qbox.NewMac(accessKey, secretKey)
|
||||||
|
|
||||||
|
cfg := storage.Config{
|
||||||
|
// 是否使用https域名进行资源管理
|
||||||
|
UseHTTPS: false,
|
||||||
|
}
|
||||||
|
// 指定空间所在的区域,如果不指定将自动探测
|
||||||
|
// 如果没有特殊需求,默认不需要指定
|
||||||
|
//cfg.Zone=&storage.ZoneHuabei
|
||||||
|
bucketManager := storage.NewBucketManager(mac, &cfg)
|
||||||
|
|
||||||
|
//每个batch的操作数量不可以超过1000个,如果总数量超过1000,需要分批发送
|
||||||
|
keys := []string{
|
||||||
|
"github1.png",
|
||||||
|
"github2.png",
|
||||||
|
"github3.png",
|
||||||
|
"github4.png",
|
||||||
|
"github5.png",
|
||||||
|
}
|
||||||
|
deleteOps := make([]string, 0, len(keys))
|
||||||
|
for _, key := range keys {
|
||||||
|
deleteOps = append(deleteOps, storage.URIDelete(bucket, key))
|
||||||
|
}
|
||||||
|
rets, err := bucketManager.Batch(deleteOps)
|
||||||
|
if err != nil {
|
||||||
|
// 遇到错误
|
||||||
|
if _, ok := err.(*rpc.ErrorInfo); ok {
|
||||||
|
for _, ret := range rets {
|
||||||
|
// 200 为成功
|
||||||
|
fmt.Printf("%d\n", ret.Code)
|
||||||
|
if ret.Code != 200 {
|
||||||
|
fmt.Printf("%s\n", ret.Data.Error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
fmt.Printf("batch error, %s", err)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// 完全成功
|
||||||
|
for _, ret := range rets {
|
||||||
|
// 200 为成功
|
||||||
|
fmt.Printf("%d\n", ret.Code)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
66
vendor/github.com/qiniu/api.v7/examples/rs_batch_deleteAfterDays.go
generated
vendored
Normal file
66
vendor/github.com/qiniu/api.v7/examples/rs_batch_deleteAfterDays.go
generated
vendored
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"github.com/qiniu/api.v7/auth/qbox"
|
||||||
|
"github.com/qiniu/api.v7/storage"
|
||||||
|
"github.com/qiniu/x/rpc.v7"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
accessKey = os.Getenv("QINIU_ACCESS_KEY")
|
||||||
|
secretKey = os.Getenv("QINIU_SECRET_KEY")
|
||||||
|
bucket = os.Getenv("QINIU_TEST_BUCKET")
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
mac := qbox.NewMac(accessKey, secretKey)
|
||||||
|
|
||||||
|
cfg := storage.Config{
|
||||||
|
// 是否使用https域名进行资源管理
|
||||||
|
UseHTTPS: false,
|
||||||
|
}
|
||||||
|
// 指定空间所在的区域,如果不指定将自动探测
|
||||||
|
// 如果没有特殊需求,默认不需要指定
|
||||||
|
//cfg.Zone=&storage.ZoneHuabei
|
||||||
|
bucketManager := storage.NewBucketManager(mac, &cfg)
|
||||||
|
|
||||||
|
//每个batch的操作数量不可以超过1000个,如果总数量超过1000,需要分批发送
|
||||||
|
expireKeys := map[string]int{
|
||||||
|
"github1.png": 7,
|
||||||
|
"github2.png": 8,
|
||||||
|
"github3.png": 9,
|
||||||
|
"github4.png": 10,
|
||||||
|
"github5.png": 11,
|
||||||
|
}
|
||||||
|
expireOps := make([]string, 0, len(expireKeys))
|
||||||
|
for key, expire := range expireKeys {
|
||||||
|
expireOps = append(expireOps, storage.URIDeleteAfterDays(bucket, key, expire))
|
||||||
|
}
|
||||||
|
rets, err := bucketManager.Batch(expireOps)
|
||||||
|
if err != nil {
|
||||||
|
// 遇到错误
|
||||||
|
if _, ok := err.(*rpc.ErrorInfo); ok {
|
||||||
|
for _, ret := range rets {
|
||||||
|
// 200 为成功
|
||||||
|
fmt.Printf("%d\n", ret.Code)
|
||||||
|
if ret.Code != 200 {
|
||||||
|
fmt.Printf("%s\n", ret.Data.Error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
fmt.Printf("batch error, %s", err)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// 完全成功
|
||||||
|
for _, ret := range rets {
|
||||||
|
// 200 为成功
|
||||||
|
fmt.Printf("%d\n", ret.Code)
|
||||||
|
if ret.Code != 200 {
|
||||||
|
fmt.Printf("%s\n", ret.Data.Error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
67
vendor/github.com/qiniu/api.v7/examples/rs_batch_move.go
generated
vendored
Normal file
67
vendor/github.com/qiniu/api.v7/examples/rs_batch_move.go
generated
vendored
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"github.com/qiniu/api.v7/auth/qbox"
|
||||||
|
"github.com/qiniu/api.v7/storage"
|
||||||
|
"github.com/qiniu/x/rpc.v7"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
accessKey = os.Getenv("QINIU_ACCESS_KEY")
|
||||||
|
secretKey = os.Getenv("QINIU_SECRET_KEY")
|
||||||
|
bucket = os.Getenv("QINIU_TEST_BUCKET")
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
mac := qbox.NewMac(accessKey, secretKey)
|
||||||
|
|
||||||
|
cfg := storage.Config{
|
||||||
|
// 是否使用https域名进行资源管理
|
||||||
|
UseHTTPS: false,
|
||||||
|
}
|
||||||
|
// 指定空间所在的区域,如果不指定将自动探测
|
||||||
|
// 如果没有特殊需求,默认不需要指定
|
||||||
|
//cfg.Zone=&storage.ZoneHuabei
|
||||||
|
bucketManager := storage.NewBucketManager(mac, &cfg)
|
||||||
|
|
||||||
|
//每个batch的操作数量不可以超过1000个,如果总数量超过1000,需要分批发送
|
||||||
|
srcBucket := bucket
|
||||||
|
destBucket := bucket
|
||||||
|
force := true
|
||||||
|
moveKeys := map[string]string{
|
||||||
|
"github1.png": "github1-move.png",
|
||||||
|
"github2.png": "github2-move.png",
|
||||||
|
"github3.png": "github3-move.png",
|
||||||
|
"github4.png": "github4-move.png",
|
||||||
|
"github5.png": "github5-move.png",
|
||||||
|
}
|
||||||
|
moveOps := make([]string, 0, len(moveKeys))
|
||||||
|
for srcKey, destKey := range moveKeys {
|
||||||
|
moveOps = append(moveOps, storage.URIMove(srcBucket, srcKey, destBucket, destKey, force))
|
||||||
|
}
|
||||||
|
rets, err := bucketManager.Batch(moveOps)
|
||||||
|
if err != nil {
|
||||||
|
// 遇到错误
|
||||||
|
if _, ok := err.(*rpc.ErrorInfo); ok {
|
||||||
|
for _, ret := range rets {
|
||||||
|
// 200 为成功
|
||||||
|
fmt.Printf("%d\n", ret.Code)
|
||||||
|
if ret.Code != 200 {
|
||||||
|
fmt.Printf("%s\n", ret.Data.Error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
fmt.Printf("batch error, %s", err)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// 完全成功
|
||||||
|
for _, ret := range rets {
|
||||||
|
// 200 为成功
|
||||||
|
fmt.Printf("%d\n", ret.Code)
|
||||||
|
fmt.Printf("%v\n", ret.Data)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
66
vendor/github.com/qiniu/api.v7/examples/rs_batch_stat.go
generated
vendored
Normal file
66
vendor/github.com/qiniu/api.v7/examples/rs_batch_stat.go
generated
vendored
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"github.com/qiniu/api.v7/auth/qbox"
|
||||||
|
"github.com/qiniu/api.v7/storage"
|
||||||
|
"github.com/qiniu/x/rpc.v7"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
accessKey = os.Getenv("QINIU_ACCESS_KEY")
|
||||||
|
secretKey = os.Getenv("QINIU_SECRET_KEY")
|
||||||
|
bucket = os.Getenv("QINIU_TEST_BUCKET")
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
mac := qbox.NewMac(accessKey, secretKey)
|
||||||
|
|
||||||
|
cfg := storage.Config{
|
||||||
|
// 是否使用https域名进行资源管理
|
||||||
|
UseHTTPS: false,
|
||||||
|
}
|
||||||
|
// 指定空间所在的区域,如果不指定将自动探测
|
||||||
|
// 如果没有特殊需求,默认不需要指定
|
||||||
|
//cfg.Zone=&storage.ZoneHuabei
|
||||||
|
bucketManager := storage.NewBucketManager(mac, &cfg)
|
||||||
|
|
||||||
|
//每个batch的操作数量不可以超过1000个,如果总数量超过1000,需要分批发送
|
||||||
|
keys := []string{
|
||||||
|
"github1.png",
|
||||||
|
"github2.png",
|
||||||
|
"github3.png",
|
||||||
|
"github4.png",
|
||||||
|
"github5.png",
|
||||||
|
}
|
||||||
|
statOps := make([]string, 0, len(keys))
|
||||||
|
for _, key := range keys {
|
||||||
|
statOps = append(statOps, storage.URIStat(bucket, key))
|
||||||
|
}
|
||||||
|
rets, err := bucketManager.Batch(statOps)
|
||||||
|
if err != nil {
|
||||||
|
// 遇到错误
|
||||||
|
if _, ok := err.(*rpc.ErrorInfo); ok {
|
||||||
|
for _, ret := range rets {
|
||||||
|
// 200 为成功
|
||||||
|
fmt.Printf("%d\n", ret.Code)
|
||||||
|
if ret.Code != 200 {
|
||||||
|
fmt.Printf("%s\n", ret.Data.Error)
|
||||||
|
} else {
|
||||||
|
fmt.Printf("%v\n", ret.Data)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
fmt.Printf("batch error, %s", err)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// 完全成功
|
||||||
|
for _, ret := range rets {
|
||||||
|
// 200 为成功
|
||||||
|
fmt.Printf("%d\n", ret.Code)
|
||||||
|
fmt.Printf("%v\n", ret.Data)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
36
vendor/github.com/qiniu/api.v7/examples/rs_change_mime.go
generated
vendored
Normal file
36
vendor/github.com/qiniu/api.v7/examples/rs_change_mime.go
generated
vendored
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"github.com/qiniu/api.v7/auth/qbox"
|
||||||
|
"github.com/qiniu/api.v7/storage"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
accessKey = os.Getenv("QINIU_ACCESS_KEY")
|
||||||
|
secretKey = os.Getenv("QINIU_SECRET_KEY")
|
||||||
|
bucket = os.Getenv("QINIU_TEST_BUCKET")
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
mac := qbox.NewMac(accessKey, secretKey)
|
||||||
|
|
||||||
|
cfg := storage.Config{
|
||||||
|
// 是否使用https域名进行资源管理
|
||||||
|
UseHTTPS: false,
|
||||||
|
}
|
||||||
|
// 指定空间所在的区域,如果不指定将自动探测
|
||||||
|
// 如果没有特殊需求,默认不需要指定
|
||||||
|
//cfg.Zone=&storage.ZoneHuabei
|
||||||
|
bucketManager := storage.NewBucketManager(mac, &cfg)
|
||||||
|
|
||||||
|
key := "github.png"
|
||||||
|
newMime := "image/x-png"
|
||||||
|
err := bucketManager.ChangeMime(bucket, key, newMime)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
36
vendor/github.com/qiniu/api.v7/examples/rs_change_type.go
generated
vendored
Normal file
36
vendor/github.com/qiniu/api.v7/examples/rs_change_type.go
generated
vendored
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"github.com/qiniu/api.v7/auth/qbox"
|
||||||
|
"github.com/qiniu/api.v7/storage"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
accessKey = os.Getenv("QINIU_ACCESS_KEY")
|
||||||
|
secretKey = os.Getenv("QINIU_SECRET_KEY")
|
||||||
|
bucket = os.Getenv("QINIU_TEST_BUCKET")
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
mac := qbox.NewMac(accessKey, secretKey)
|
||||||
|
|
||||||
|
cfg := storage.Config{
|
||||||
|
// 是否使用https域名进行资源管理
|
||||||
|
UseHTTPS: false,
|
||||||
|
}
|
||||||
|
// 指定空间所在的区域,如果不指定将自动探测
|
||||||
|
// 如果没有特殊需求,默认不需要指定
|
||||||
|
//cfg.Zone=&storage.ZoneHuabei
|
||||||
|
bucketManager := storage.NewBucketManager(mac, &cfg)
|
||||||
|
|
||||||
|
key := "github.png"
|
||||||
|
fileType := 1 // 0 表示普通存储,1表示低频存储
|
||||||
|
err := bucketManager.ChangeType(bucket, key, fileType)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
42
vendor/github.com/qiniu/api.v7/examples/rs_copy.go
generated
vendored
Normal file
42
vendor/github.com/qiniu/api.v7/examples/rs_copy.go
generated
vendored
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"github.com/qiniu/api.v7/auth/qbox"
|
||||||
|
"github.com/qiniu/api.v7/storage"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
accessKey = os.Getenv("QINIU_ACCESS_KEY")
|
||||||
|
secretKey = os.Getenv("QINIU_SECRET_KEY")
|
||||||
|
bucket = os.Getenv("QINIU_TEST_BUCKET")
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
mac := qbox.NewMac(accessKey, secretKey)
|
||||||
|
|
||||||
|
cfg := storage.Config{
|
||||||
|
// 是否使用https域名进行资源管理
|
||||||
|
UseHTTPS: false,
|
||||||
|
}
|
||||||
|
// 指定空间所在的区域,如果不指定将自动探测
|
||||||
|
// 如果没有特殊需求,默认不需要指定
|
||||||
|
//cfg.Zone=&storage.ZoneHuabei
|
||||||
|
bucketManager := storage.NewBucketManager(mac, &cfg)
|
||||||
|
|
||||||
|
srcBucket := "if-pbl"
|
||||||
|
srcKey := "github.png"
|
||||||
|
//目标空间可以和源空间相同,但是不能为跨机房的空间
|
||||||
|
destBucket := srcBucket
|
||||||
|
//目标文件名可以和源文件名相同,也可以不同
|
||||||
|
destKey := "github-new.png"
|
||||||
|
//如果目标文件存在,是否强制覆盖,如果不覆盖,默认返回614 file exists
|
||||||
|
force := false
|
||||||
|
err := bucketManager.Copy(srcBucket, srcKey, destBucket, destKey, force)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
35
vendor/github.com/qiniu/api.v7/examples/rs_delete.go
generated
vendored
Normal file
35
vendor/github.com/qiniu/api.v7/examples/rs_delete.go
generated
vendored
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"github.com/qiniu/api.v7/auth/qbox"
|
||||||
|
"github.com/qiniu/api.v7/storage"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
accessKey = os.Getenv("QINIU_ACCESS_KEY")
|
||||||
|
secretKey = os.Getenv("QINIU_SECRET_KEY")
|
||||||
|
bucket = os.Getenv("QINIU_TEST_BUCKET")
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
mac := qbox.NewMac(accessKey, secretKey)
|
||||||
|
|
||||||
|
cfg := storage.Config{
|
||||||
|
// 是否使用https域名进行资源管理
|
||||||
|
UseHTTPS: false,
|
||||||
|
}
|
||||||
|
// 指定空间所在的区域,如果不指定将自动探测
|
||||||
|
// 如果没有特殊需求,默认不需要指定
|
||||||
|
//cfg.Zone=&storage.ZoneHuabei
|
||||||
|
bucketManager := storage.NewBucketManager(mac, &cfg)
|
||||||
|
|
||||||
|
key := "github.png"
|
||||||
|
err := bucketManager.Delete(bucket, key)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
36
vendor/github.com/qiniu/api.v7/examples/rs_deleteAfterDays.go
generated
vendored
Normal file
36
vendor/github.com/qiniu/api.v7/examples/rs_deleteAfterDays.go
generated
vendored
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"github.com/qiniu/api.v7/auth/qbox"
|
||||||
|
"github.com/qiniu/api.v7/storage"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
accessKey = os.Getenv("QINIU_ACCESS_KEY")
|
||||||
|
secretKey = os.Getenv("QINIU_SECRET_KEY")
|
||||||
|
bucket = os.Getenv("QINIU_TEST_BUCKET")
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
mac := qbox.NewMac(accessKey, secretKey)
|
||||||
|
|
||||||
|
cfg := storage.Config{
|
||||||
|
// 是否使用https域名进行资源管理
|
||||||
|
UseHTTPS: false,
|
||||||
|
}
|
||||||
|
// 指定空间所在的区域,如果不指定将自动探测
|
||||||
|
// 如果没有特殊需求,默认不需要指定
|
||||||
|
//cfg.Zone=&storage.ZoneHuabei
|
||||||
|
bucketManager := storage.NewBucketManager(mac, &cfg)
|
||||||
|
|
||||||
|
key := "github.png"
|
||||||
|
days := 7
|
||||||
|
err := bucketManager.DeleteAfterDays(bucket, key, days)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user