refactor: eiblog
@@ -1,16 +1,10 @@
|
|||||||
.git
|
# Ignore all files and dirs
|
||||||
conf
|
*
|
||||||
vendor
|
|
||||||
setting
|
# Unignore files or dirs
|
||||||
docs
|
!build
|
||||||
static/*.*
|
!bin
|
||||||
!static/favicon.ico
|
!conf
|
||||||
**/.DS_Store
|
!CHANGELOG.md
|
||||||
Dockerfile
|
!LICENSE
|
||||||
glide.yaml
|
!README.md
|
||||||
glide.lock
|
|
||||||
*.yml
|
|
||||||
*.go
|
|
||||||
*.sh
|
|
||||||
.gitignore
|
|
||||||
.dockerignore
|
|
||||||
|
|||||||
21
.gitignore
vendored
@@ -1,5 +1,18 @@
|
|||||||
**/.DS_Store
|
# Binaries for programs and plugins
|
||||||
*.exe
|
*.exe
|
||||||
conf/ssl/domain.*
|
*.exe~
|
||||||
eiblog
|
*.dll
|
||||||
static/*.*
|
*.so
|
||||||
|
*.dylib
|
||||||
|
*.DS_Store
|
||||||
|
|
||||||
|
# Test binary, built with `go test -c`
|
||||||
|
*.test
|
||||||
|
|
||||||
|
# Output of the go coverage tool, specifically when used with LiteIDE
|
||||||
|
*.out
|
||||||
|
|
||||||
|
# Dependency directories (remove the comment below to include it)
|
||||||
|
# vendor/
|
||||||
|
bin
|
||||||
|
|
||||||
|
|||||||
110
CHANGELOG.md
@@ -2,110 +2,16 @@
|
|||||||
|
|
||||||
All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
|
All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
|
||||||
|
|
||||||
### [1.4.9](https://github.com/eiblog/eiblog/compare/v1.4.8...v1.4.9) (2019-12-18)
|
## [1.1.0](https://github.com/deepzz0/appdemo/compare/v1.0.0...v1.1.0) (2020-12-18)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* **docker:** make build, build docker image ([3ac2b8b](https://github.com/deepzz0/appdemo/commit/3ac2b8b2efadf024dfcf58e7ef8341b1a89cf1b1))
|
||||||
|
|
||||||
|
|
||||||
### Bug Fixes
|
### Bug Fixes
|
||||||
|
|
||||||
* **disqus:** connect reset by peer ([1bdfb6a](https://github.com/eiblog/eiblog/commit/1bdfb6a))
|
* config path fixed [#1](https://github.com/deepzz0/appdemo/issues/1) ([4343eb4](https://github.com/deepzz0/appdemo/commit/4343eb44e8fffc6825be57393e024c75c4f68b7b))
|
||||||
|
|
||||||
### v1.4.4 (2018-05-07)
|
## 1.0.0 (2020-10-31)
|
||||||
|
|
||||||
* 修复基础评论分钟数计算错误
|
|
||||||
* let's encrypt v2证书内嵌ct,故移除有关ct内容
|
|
||||||
|
|
||||||
### v1.4.3 (2018-02-09)
|
|
||||||
|
|
||||||
* 修复博客初始化后,about 页面不能够评论 #6
|
|
||||||
* 修复编辑专题,按钮显示“添加专题”错误
|
|
||||||
* 优化“添加文章”从同步改为异步推送:feed,es,disqus。速度显著提升
|
|
||||||
* (**重要*)头像图片从 avatar.jpg 改为 avatar.png(透明)
|
|
||||||
* docker-compose.yml mongodb 去掉端口映射,防止用户将端口暴露至外网
|
|
||||||
* session key 每次重启随机生成等一些细节的修复
|
|
||||||
|
|
||||||
### v1.4.2 (2018-01-25)
|
|
||||||
|
|
||||||
* fix archive page bug
|
|
||||||
|
|
||||||
### 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)
|
|
||||||
|
|
||||||
* 更改 app.yml 配置项,将大部分配置归在 general 常规配置下。注意,部署时请先更新 app.yml。
|
|
||||||
* 静态文件采用动态渲染,即用户不再需要管理 view、static 目录。
|
|
||||||
* 通过 acme.sh 使用双证书啦,可到 Makefile 查看相关信息。
|
|
||||||
* 使用 autocert 自动生成证书功能,从此再也不用担心证书过期,移步 [证书更新](https://github.com/eiblog/eiblog/blob/master/docs/autocert.md)。
|
|
||||||
* 开启配置项 enablehttps, 将自动重定向 http 到 https 啦。
|
|
||||||
* disqus.js 文件由配置指定,请看 app.yml 下的 disqus 相关配置。
|
|
||||||
|
|
||||||
### v1.2.0 (2017-06-14)
|
|
||||||
|
|
||||||
* 更新评论功能,基础评论 0 回复也可评论了。
|
|
||||||
* disqus.js 文件由博主自行更新。
|
|
||||||
* 更正描述 README.md 描述错误 [#4f996](https://github.com/eiblog/eiblog/commit/4f9965b6bdefe087dd0805c1840afcb2752cd155)。
|
|
||||||
* docker 镜像版本化。
|
|
||||||
|
|
||||||
### v1.1.3 (2017-05-12)
|
|
||||||
|
|
||||||
* 更新 disqus_78bca4.js 到 disqus_921d24.js,具体请参考 docs/install.md
|
|
||||||
* 更新 vendor
|
|
||||||
|
|
||||||
### v1.1.2 (2017-03-08)
|
|
||||||
|
|
||||||
* 解决添加文章描述错误的bug
|
|
||||||
* 添加vendor目录
|
|
||||||
* 添加文档docs目录
|
|
||||||
* 删除多余注释
|
|
||||||
|
|
||||||
### v1.1.1 (2017-02-07)
|
|
||||||
|
|
||||||
* 添加文章描述功能。
|
|
||||||
* 修复评论`jQuery`文件引用错误。
|
|
||||||
* 修复`.travis.yml`描述错误。
|
|
||||||
|
|
||||||
### v1.0.0 (2016-01-09)
|
|
||||||
|
|
||||||
首次发布版本
|
|
||||||
|
|
||||||
* 全站`HTTPS`设计,安全、极速。
|
|
||||||
* `Elasticsearch`博客搜索系统。
|
|
||||||
* 开源`Typecho`完整博客后台。
|
|
||||||
* 全功能`Markdown`编辑器。
|
|
||||||
* 异步`Google analysts`分析统计。
|
|
||||||
* `Disqus`评论系统。
|
|
||||||
* 后台直接对接七牛`CDN`。
|
|
||||||
|
|||||||
10
Dockerfile
@@ -1,10 +0,0 @@
|
|||||||
FROM alpine
|
|
||||||
MAINTAINER deepzz <deepzz.qi@gmail.com>
|
|
||||||
|
|
||||||
RUN apk add --update --no-cache ca-certificates
|
|
||||||
ADD static/tzdata/Shanghai /etc/localtime
|
|
||||||
|
|
||||||
COPY . /eiblog
|
|
||||||
EXPOSE 9000
|
|
||||||
WORKDIR /eiblog
|
|
||||||
CMD ["sh","-c","/eiblog/eiblog"]
|
|
||||||
3
LICENSE
@@ -1,6 +1,6 @@
|
|||||||
The MIT License (MIT)
|
The MIT License (MIT)
|
||||||
|
|
||||||
Copyright (c) 2017 deepzz deepzz.qi@gmail.com
|
Copyright (c) 2020-NOW deepzz0 <deepzz.qi@gmail.com>
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
@@ -19,3 +19,4 @@ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|||||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
THE SOFTWARE.
|
THE SOFTWARE.
|
||||||
|
|
||||||
|
|||||||
85
Makefile
@@ -1,73 +1,28 @@
|
|||||||
.PHONY: test build deploy dist gencert dhparams ssticket makedir clean
|
.PHONY: demo build swag
|
||||||
# use aliyun dns api to auto renew cert.
|
|
||||||
# env:
|
|
||||||
# export Ali_Key="sdfsdfsdfljlbjkljlkjsdfoiwje"
|
|
||||||
# export Ali_Secret="jlsdflanljkljlfdsaklkjflsa"
|
|
||||||
|
|
||||||
docker_registry?=registry.cn-hangzhou.aliyuncs.com
|
REGISTRY=localhost
|
||||||
acme?=~/.acme.sh
|
IMAGE_TAG=`git describe --tags`
|
||||||
acme.sh?=$(acme)/acme.sh
|
|
||||||
config?=/data/eiblog/conf
|
|
||||||
|
|
||||||
|
swag:
|
||||||
|
@scripts/swag_init.sh
|
||||||
|
|
||||||
test:
|
_app:
|
||||||
|
@scripts/new_app.sh
|
||||||
|
|
||||||
mongodb:
|
# below you should write
|
||||||
@if ! docker ps | grep mongodb; then \
|
|
||||||
docker run -d --name mongodb -v mongo-data:/data/db -p 27018:27017 mongo:3.2; \
|
|
||||||
fi
|
|
||||||
|
|
||||||
run: mongodb
|
# run blog app
|
||||||
@echo "run eiblog..."
|
blog:
|
||||||
@go build && ./eiblog
|
@scripts/run_app.sh blog
|
||||||
|
|
||||||
|
# run backup app
|
||||||
|
backup:
|
||||||
|
@scripts/run_app.sh backup
|
||||||
|
|
||||||
|
# build docker
|
||||||
build:
|
build:
|
||||||
@echo "go build..."
|
@scripts/run_build.sh $(REGISTRY) $(IMAGE_TAG)
|
||||||
@CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build && \
|
|
||||||
docker build -t $(docker_registry)/deepzz/eiblog:latest .
|
|
||||||
|
|
||||||
deploy:build
|
# protoc
|
||||||
@docker push $(docker_registry)/deepzz/eiblog:latest
|
protoc:
|
||||||
|
@cd pkg/proto && make protoc
|
||||||
dist:
|
|
||||||
@./dist.sh
|
|
||||||
|
|
||||||
gencert:makedir
|
|
||||||
@if [ ! -n "$(sans)" ]; then \
|
|
||||||
printf "Need one argument [sans=params]\n"; \
|
|
||||||
printf "example: sans=\"-d domain -d *.domain\"\n"; \
|
|
||||||
exit 1; \
|
|
||||||
fi; \
|
|
||||||
if [ ! -n "$(cn)" ]; then \
|
|
||||||
printf "Need one argument [cn=params]\n"; \
|
|
||||||
printf "example: cn=domain\n"; \
|
|
||||||
exit 1; \
|
|
||||||
fi
|
|
||||||
@if [ ! -f $(acme.sh) ]; then \
|
|
||||||
curl https://get.acme.sh | sh; \
|
|
||||||
fi
|
|
||||||
|
|
||||||
@echo "generate rsa cert..."
|
|
||||||
@$(acme.sh) --force --issue --dns dns_ali $(sans) \
|
|
||||||
--renew-hook "$(acme.sh) --install-cert -d $(cn) \
|
|
||||||
--key-file $(config)/ssl/domain.rsa.key \
|
|
||||||
--fullchain-file $(config)/ssl/domain.rsa.pem \
|
|
||||||
--reloadcmd \"service nginx force-reload\""
|
|
||||||
|
|
||||||
@echo "generate ecc cert..."
|
|
||||||
@$(acme.sh) --force --issue --dns dns_ali $(sans) -k ec-256 \
|
|
||||||
--renew-hook "$(acme.sh) --install-cert -d $(cn) --ecc \
|
|
||||||
--key-file $(config)/ssl/domain.ecc.key \
|
|
||||||
--fullchain-file $(config)/ssl/domain.ecc.pem \
|
|
||||||
--reloadcmd \"service nginx force-reload\""
|
|
||||||
|
|
||||||
dhparams:
|
|
||||||
@openssl dhparam -out $(config)/ssl/dhparams.pem 2048
|
|
||||||
|
|
||||||
ssticket:
|
|
||||||
@openssl rand 48 > $(config)/ssl/session_ticket.key
|
|
||||||
|
|
||||||
makedir:
|
|
||||||
@mkdir -p $(config)/ssl
|
|
||||||
|
|
||||||
clean:
|
|
||||||
|
|||||||
126
README.md
@@ -1,94 +1,56 @@
|
|||||||
# EiBlog [](https://travis-ci.org/eiblog/eiblog) [](LICENSE.md) [](https://github.com/eiblog/eiblog/releases)
|
# APP Demo
|
||||||
|
|
||||||
> 系统根据[https://imququ.com](https://imququ.com)一系列文章和方向进行搭建,期间获得了QuQu的很大帮助,在此表示感谢。
|
Since there are many Web apps developed, a common development template is put together here.
|
||||||
|
|
||||||
用过其它博客系统,不喜欢,不够轻,不够快!自己做过共两款博客系统,完美主义的我(毕竟处女座)也实在是不想再在这件事情上过多纠结了。`Eiblog` 应该是一个比较稳定的博客系统,且是博主以后使用的博客系统,稳定性和维护你是不用担心的,唯独该系统部署过程太过复杂,并且不推荐没有计算机知识的朋友搭建,欢迎咨询。该博客的个中优点(明显快,安全),等你体验。
|
### New app
|
||||||
|
1、You need copy `appdemo` to your `GOPATH` and rename:
|
||||||
<!--more-->
|
|
||||||
|
|
||||||
### 介绍
|
|
||||||
|
|
||||||
整个博客系统涉及到模块如下:
|
|
||||||
|
|
||||||
* 自动更新证书:
|
|
||||||
* 接入 [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)。
|
|
||||||
* `MongoDB`,博客采用 mongodb 作为存储数据库。
|
|
||||||
* `Elasticsearch`,采用 `elasticsearch` 作为博客的站内搜索,尽管占用内存稍高。
|
|
||||||
* `Disqus`,作为博客评论系统,国内大部分被墙,故实现两种评论方式。
|
|
||||||
* `Nginx`,作为反向代理服务器,并做相关 `http header` 和证书的设置。
|
|
||||||
* `Google Analytics`,作为博客系统的数据分析统计工具。
|
|
||||||
* `七牛 CDN`,作为博客系统的静态文件存储,博文的图片附件什么上传至这里。
|
|
||||||
|
|
||||||
### 图片展示
|
|
||||||
|
|
||||||
可以容易的看到 [httpsecurityreport](https://httpsecurityreport.com/?report=deepzz.com) 评分`96`,[ssllabs](https://www.ssllabs.com/ssltest/analyze.html?d=deepzz.com&latest) 评分`A+`,[myssl](https://myssl.com/deepzz.com) 评分`A+`,堪称完美。这些安全的相关配置会在后面的部署过程中接触到。
|
|
||||||
|
|
||||||
相关图片展示:
|
|
||||||

|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
> `注`:图片1,图片2是博客界面,图片3是后台界面,图片4是内存占用。
|
|
||||||
|
|
||||||
### 极速体验
|
|
||||||
`eiblog` 默认监听 `:9000` 端口,默认连接 `MongoDB` 地址 `mongodb:27017`,默认连接 `Elasticsearch` 地址 `http://elasticsearch:9200`。
|
|
||||||
|
|
||||||
1、手动启动执行
|
|
||||||
|
|
||||||
1. 到 [这里](https://github.com/eiblog/eiblog/releases) 下载对应平台 `.tar.gz` 文件。
|
|
||||||
2. 搭建 `MongoDB`(必须)和 `Elasticsearch`(可选)服务,正式部署需要。
|
|
||||||
3. `MongoDB` 服务地址也可通过环境变量指定连接地址如:`export EIBLOG_MGO_ADDR=127.0.0.1:27017`。
|
|
||||||
4. 执行 `./eiblog`,运行博客系统。看到 `...Listening and serving HTTP on :9000` 代表运行成功了。
|
|
||||||
|
|
||||||
默认监听 `HTTP 9000` 端口,后台 `/admin/login`,默认账号密码均为 `deepzz`。更多详细请查阅 [安装部署](https://github.com/eiblog/eiblog/blob/master/docs/install.md) 文档。
|
|
||||||
|
|
||||||
2、Docker 与 Go 环境
|
|
||||||
|
|
||||||
如果你有 `docker` 和 `go` 环境,可直接使用如下命令启动:
|
|
||||||
```
|
```
|
||||||
$ EIBLOG_MGO_ADDR=127.0.0.1:27017 make run
|
$ git clone git@github.com:deepzz0/appdemo.git <app name>
|
||||||
```
|
```
|
||||||
请提前指定 `mongodb` 的连接地址。该命令会启动一个 `mognodb` 容器,然后编译 `eiblog` 并运行。
|
|
||||||
|
|
||||||
### 特色功能
|
3、Enter your app, run:
|
||||||
|
```
|
||||||
|
$ cd <app name>
|
||||||
|
$ make _app
|
||||||
|
```
|
||||||
|
|
||||||
作为博主之心血之作,`Eiblog` 实现了什么功能,有什么特点,做了什么优化呢?
|
3、Push the code to new repo:
|
||||||
|
```
|
||||||
|
$ git add .
|
||||||
|
$ git commit -m "init repo"
|
||||||
|
$ git remote add origin <your repo>
|
||||||
|
$ git push -u origin master
|
||||||
|
```
|
||||||
|
|
||||||
1. 系统目前只有 `首页`、`专题`、`归档`、`友链`、`关于`、`搜索` 界面。相信已经可以满足大部分用户的需求。
|
4、`make demo` you can start your web app.
|
||||||
2. `.js`、`.css` 等静态文件本地存储,小图片 base64 内置到 css 中,不会产生网络所带来的延迟,加速网页访问。版本控制方式,动态更新静态文件。
|
|
||||||
3. 采用谷歌统计,并实现异步(将访问信息发给后端,后端提交给谷歌)统计,加速访问速度。
|
|
||||||
4. 采用直接缓存 markdown 转过的 html 文档的方式,加速后端处理。响应速度均在 3ms 以内,真正极速。
|
|
||||||
5. 通过 Nginx 的配置,开启压缩缩小传输量,服务器传输证书链、开启 `Session Resumption`、`Session Ticket`、`OCSP Stapling `等加速证书握手,再次提高速度。
|
|
||||||
* `CDN`,使用七牛融合CDN,并 `https` 化,实现全站 `https`。七牛可申请免费证书了。
|
|
||||||
* `CT`,证书透明度检测,提供一个开放的审计和监控系统。可以让任何域名所有者或者 CA 确定证书是否被错误签发或者被恶意使用,从而提高 HTTPS 网站的安全性。
|
|
||||||
* `OSCP`,在线证书状态协议。用来检验证书合法性的在线查询服务.
|
|
||||||
* `HSTS`,强制客户端(如浏览器)使用 HTTPS 与服务器创建连接。可以很好的解决 HTTPS 降级攻击。
|
|
||||||
* `HPKP`,HTTP 公钥固定扩展,防范由「伪造或不正当手段获得网站证书」造成的中间人攻击。该功能让我们选择信任哪些`CA`。请不要轻易尝试 Nginx 线上运行,因为该配置目前只指定了 Letsencrypt X3 和 TrustAsia G5 证书 pin-sha256。
|
|
||||||
* `SSL Protocols`,罗列支持的 `TLS` 协议,SSLv3 被证实是不安全的。
|
|
||||||
* `SSL dhparam`,迪菲赫尔曼密钥交换。
|
|
||||||
* `Cipher suite`,罗列服务器支持加密套件。
|
|
||||||
6. 文章评论数量(不重要)后端跑定时脚本,定时更新,所以有时评论数是不对的。这样减少了 api 调用,又再次达到加速访问的目的。
|
|
||||||
7. 针对 `disqus` 被墙原因,实现 [Jerry Qu](https://imququ.com) 的另类评论方式,保证评论的流畅。
|
|
||||||
8. 开源 `Typecho` 完整后台系统,全功能 `markdown` 编辑器,让你体验什么是简洁清爽。
|
|
||||||
9. 博客后台直接对接 `七牛 SDK`,实现后台上传文件和删除文件的简单功能。
|
|
||||||
10. 采用 `Elasticsearch` 作为站内搜索,结合 `google opensearch` 功能,搜索更加自然。
|
|
||||||
11. 自动备份数据库数据到七牛云。
|
|
||||||
|
|
||||||
### 文档
|
### Development
|
||||||
|
|
||||||
* [证书更新](https://github.com/eiblog/eiblog/blob/master/docs/autocert.md)
|
**Step1**
|
||||||
* [安装部署](https://github.com/eiblog/eiblog/blob/master/docs/install.md)
|
|
||||||
* [写作需知](https://github.com/eiblog/eiblog/blob/master/docs/writing.md)
|
Understand the directory.
|
||||||
* [好玩的功能](https://github.com/eiblog/eiblog/blob/master/docs/amusing.md)
|
|
||||||
* [关于备份](https://github.com/eiblog/backup)
|
```
|
||||||
|
.
|
||||||
|
├── build # Packaging and CI.
|
||||||
|
├── cmd # Main applications for this app.
|
||||||
|
├── conf # Static configuration file.
|
||||||
|
├── docs # Design and user documents.
|
||||||
|
├── pkg # Library code that's ok to use by external applications.
|
||||||
|
├── scripts # Scripts to perform various build, install, analysis, etc operations.
|
||||||
|
├── website # APP's website data.
|
||||||
|
├── CHANGELOG.md # Record version change.
|
||||||
|
├── LICENSE # Open source license
|
||||||
|
├── Makefile # Makefile: call scripts
|
||||||
|
├── README.md # Read me docs.
|
||||||
|
└── go.mod # Go mod file.
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
**Step2**
|
||||||
|
|
||||||
|
Code in pkg and cmd or website.
|
||||||
|
|
||||||
### 成功搭建者博客
|
|
||||||
|
|
||||||
* [https://blog.netcj.com](https://blog.netcj.com) - Razeen's Blog
|
|
||||||
|
|
||||||
如果你的博客使用`Eiblog`搭建,你可以在 [这里](https://github.com/eiblog/eiblog/issues/1) 提交网址。
|
|
||||||
|
|||||||
448
api.go
@@ -1,448 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"net/http"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/eiblog/eiblog/setting"
|
|
||||||
"github.com/eiblog/utils/logd"
|
|
||||||
"github.com/eiblog/utils/mgo"
|
|
||||||
"github.com/gin-gonic/gin"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
// 成功
|
|
||||||
NOTICE_SUCCESS = "success"
|
|
||||||
// 注意
|
|
||||||
NOTICE_NOTICE = "notice"
|
|
||||||
// 错误
|
|
||||||
NOTICE_ERROR = "error"
|
|
||||||
)
|
|
||||||
|
|
||||||
// 全局 API
|
|
||||||
var APIs = make(map[string]func(c *gin.Context))
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
// 更新账号信息
|
|
||||||
APIs["account"] = apiAccount
|
|
||||||
// 更新博客信息
|
|
||||||
APIs["blog"] = apiBlog
|
|
||||||
// 更新密码
|
|
||||||
APIs["password"] = apiPassword
|
|
||||||
// 删除文章
|
|
||||||
APIs["post-delete"] = apiPostDelete
|
|
||||||
// 添加文章
|
|
||||||
APIs["post-add"] = apiPostAdd
|
|
||||||
// 删除专题
|
|
||||||
APIs["serie-delete"] = apiSerieDelete
|
|
||||||
// 添加专题
|
|
||||||
APIs["serie-add"] = apiSerieAdd
|
|
||||||
// 专题排序
|
|
||||||
APIs["serie-sort"] = apiSerieSort
|
|
||||||
// 删除草稿箱
|
|
||||||
APIs["draft-delete"] = apiDraftDelete
|
|
||||||
// 删除回收箱
|
|
||||||
APIs["trash-delete"] = apiTrashDelete
|
|
||||||
// 恢复回收箱
|
|
||||||
APIs["trash-recover"] = apiTrashRecover
|
|
||||||
// 上传文件
|
|
||||||
APIs["file-upload"] = apiFileUpload
|
|
||||||
// 删除文件
|
|
||||||
APIs["file-delete"] = apiFileDelete
|
|
||||||
}
|
|
||||||
|
|
||||||
// 更新账号信息,Email、PhoneNumber、Address
|
|
||||||
func apiAccount(c *gin.Context) {
|
|
||||||
e := c.PostForm("email")
|
|
||||||
pn := c.PostForm("phoneNumber")
|
|
||||||
ad := c.PostForm("address")
|
|
||||||
logd.Debug(e, pn, ad)
|
|
||||||
if (e != "" && !CheckEmail(e)) || (pn != "" && !CheckSMS(pn)) {
|
|
||||||
responseNotice(c, NOTICE_NOTICE, "参数错误", "")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
err := UpdateAccountField(mgo.M{"$set": mgo.M{"email": e, "phonen": pn, "address": ad}})
|
|
||||||
if err != nil {
|
|
||||||
logd.Error(err)
|
|
||||||
responseNotice(c, NOTICE_NOTICE, err.Error(), "")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
Ei.Email = e
|
|
||||||
Ei.PhoneN = pn
|
|
||||||
Ei.Address = ad
|
|
||||||
responseNotice(c, NOTICE_SUCCESS, "更新成功", "")
|
|
||||||
}
|
|
||||||
|
|
||||||
// 更新博客信息
|
|
||||||
func apiBlog(c *gin.Context) {
|
|
||||||
bn := c.PostForm("blogName")
|
|
||||||
bt := c.PostForm("bTitle")
|
|
||||||
ba := c.PostForm("beiAn")
|
|
||||||
st := c.PostForm("subTitle")
|
|
||||||
ss := c.PostForm("seriessay")
|
|
||||||
as := c.PostForm("archivessay")
|
|
||||||
if bn == "" || bt == "" {
|
|
||||||
responseNotice(c, NOTICE_NOTICE, "参数错误", "")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
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 {
|
|
||||||
logd.Error(err)
|
|
||||||
responseNotice(c, NOTICE_NOTICE, err.Error(), "")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
Ei.BlogName = bn
|
|
||||||
Ei.BTitle = bt
|
|
||||||
Ei.BeiAn = ba
|
|
||||||
Ei.SubTitle = st
|
|
||||||
Ei.SeriesSay = ss
|
|
||||||
Ei.ArchivesSay = as
|
|
||||||
Ei.CH <- SERIES_MD
|
|
||||||
Ei.CH <- ARCHIVE_MD
|
|
||||||
responseNotice(c, NOTICE_SUCCESS, "更新成功", "")
|
|
||||||
}
|
|
||||||
|
|
||||||
// 更新密码
|
|
||||||
func apiPassword(c *gin.Context) {
|
|
||||||
logd.Debug(c.Request.PostForm.Encode())
|
|
||||||
od := c.PostForm("old")
|
|
||||||
nw := c.PostForm("new")
|
|
||||||
cf := c.PostForm("confirm")
|
|
||||||
if nw != cf {
|
|
||||||
responseNotice(c, NOTICE_NOTICE, "两次密码输入不一致", "")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if !CheckPwd(nw) {
|
|
||||||
responseNotice(c, NOTICE_NOTICE, "密码格式错误", "")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if !VerifyPasswd(Ei.Password, Ei.Username, od) {
|
|
||||||
responseNotice(c, NOTICE_NOTICE, "原始密码不正确", "")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
newPwd := EncryptPasswd(Ei.Username, nw)
|
|
||||||
|
|
||||||
err := UpdateAccountField(mgo.M{"$set": mgo.M{"password": newPwd}})
|
|
||||||
if err != nil {
|
|
||||||
logd.Error(err)
|
|
||||||
responseNotice(c, NOTICE_NOTICE, err.Error(), "")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
Ei.Password = newPwd
|
|
||||||
responseNotice(c, NOTICE_SUCCESS, "更新成功", "")
|
|
||||||
}
|
|
||||||
|
|
||||||
// 删除文章,软删除:移入到回收箱
|
|
||||||
func apiPostDelete(c *gin.Context) {
|
|
||||||
var ids []int32
|
|
||||||
for _, v := range c.PostFormArray("cid[]") {
|
|
||||||
i, err := strconv.Atoi(v)
|
|
||||||
if err != nil || int32(i) < setting.Conf.General.StartID {
|
|
||||||
responseNotice(c, NOTICE_NOTICE, "参数错误", "")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
ids = append(ids, int32(i))
|
|
||||||
}
|
|
||||||
err := DelArticles(ids...)
|
|
||||||
if err != nil {
|
|
||||||
logd.Error(err)
|
|
||||||
responseNotice(c, NOTICE_NOTICE, err.Error(), "")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// elasticsearch
|
|
||||||
err = ElasticDelIndex(ids)
|
|
||||||
if err != nil {
|
|
||||||
logd.Error(err)
|
|
||||||
}
|
|
||||||
// TODO disqus delete
|
|
||||||
responseNotice(c, NOTICE_SUCCESS, "删除成功", "")
|
|
||||||
}
|
|
||||||
|
|
||||||
func apiPostAdd(c *gin.Context) {
|
|
||||||
var (
|
|
||||||
err error
|
|
||||||
do string
|
|
||||||
cid int
|
|
||||||
)
|
|
||||||
defer func() {
|
|
||||||
switch do {
|
|
||||||
case "auto": // 自动保存
|
|
||||||
if err != nil {
|
|
||||||
c.JSON(http.StatusOK, gin.H{"fail": FAIL, "time": time.Now().Format("15:04:05 PM"), "cid": cid})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
c.JSON(http.StatusOK, gin.H{"success": SUCCESS, "time": time.Now().Format("15:04:05 PM"), "cid": cid})
|
|
||||||
case "save", "publish": // 草稿,发布
|
|
||||||
if err != nil {
|
|
||||||
responseNotice(c, NOTICE_NOTICE, err.Error(), "")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
uri := "/admin/manage-draft"
|
|
||||||
if do == "publish" {
|
|
||||||
uri = "/admin/manage-posts"
|
|
||||||
}
|
|
||||||
c.Redirect(http.StatusFound, uri)
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
do = c.PostForm("do") // auto or save or publish
|
|
||||||
slug := c.PostForm("slug")
|
|
||||||
title := c.PostForm("title")
|
|
||||||
text := c.PostForm("text")
|
|
||||||
date := CheckDate(c.PostForm("date"))
|
|
||||||
serie := c.PostForm("serie")
|
|
||||||
tag := c.PostForm("tags")
|
|
||||||
update := c.PostForm("update")
|
|
||||||
if slug == "" || title == "" || text == "" {
|
|
||||||
err = errors.New("参数错误")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
var tags []string
|
|
||||||
if tag != "" {
|
|
||||||
tags = strings.Split(tag, ",")
|
|
||||||
}
|
|
||||||
serieid := CheckSerieID(serie)
|
|
||||||
artc := &Article{
|
|
||||||
Title: title,
|
|
||||||
Content: text,
|
|
||||||
Slug: slug,
|
|
||||||
CreateTime: date,
|
|
||||||
IsDraft: do != "publish",
|
|
||||||
Author: Ei.Username,
|
|
||||||
SerieID: serieid,
|
|
||||||
Tags: tags,
|
|
||||||
}
|
|
||||||
cid, err = strconv.Atoi(c.PostForm("cid"))
|
|
||||||
// 新文章
|
|
||||||
if err != nil || cid < 1 {
|
|
||||||
err = AddArticle(artc)
|
|
||||||
if err != nil {
|
|
||||||
logd.Error(err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
cid = int(artc.ID)
|
|
||||||
if !artc.IsDraft {
|
|
||||||
// 异步执行,快
|
|
||||||
go func() {
|
|
||||||
// elastic
|
|
||||||
ElasticIndex(artc)
|
|
||||||
// rss
|
|
||||||
DoPings(slug)
|
|
||||||
// disqus
|
|
||||||
ThreadCreate(artc)
|
|
||||||
}()
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// 旧文章
|
|
||||||
artc.ID = int32(cid)
|
|
||||||
_, a := GetArticle(artc.ID)
|
|
||||||
if a != nil {
|
|
||||||
artc.IsDraft = false
|
|
||||||
artc.Count = a.Count
|
|
||||||
artc.UpdateTime = a.UpdateTime
|
|
||||||
}
|
|
||||||
if CheckBool(update) {
|
|
||||||
artc.UpdateTime = time.Now()
|
|
||||||
}
|
|
||||||
// 数据库更新
|
|
||||||
err = UpdateArticle(mgo.M{"id": artc.ID}, artc)
|
|
||||||
if err != nil {
|
|
||||||
logd.Error(err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if !artc.IsDraft {
|
|
||||||
ReplaceArticle(a, artc)
|
|
||||||
// 异步执行,快
|
|
||||||
go func() {
|
|
||||||
// elastic
|
|
||||||
ElasticIndex(artc)
|
|
||||||
// rss
|
|
||||||
DoPings(slug)
|
|
||||||
// disqus
|
|
||||||
if a == nil {
|
|
||||||
ThreadCreate(artc)
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 只能逐一删除,专题下不能有文章
|
|
||||||
func apiSerieDelete(c *gin.Context) {
|
|
||||||
for _, v := range c.PostFormArray("mid[]") {
|
|
||||||
id, err := strconv.Atoi(v)
|
|
||||||
if err != nil || id < 1 {
|
|
||||||
responseNotice(c, NOTICE_NOTICE, err.Error(), "")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
err = DelSerie(int32(id))
|
|
||||||
if err != nil {
|
|
||||||
logd.Error(err)
|
|
||||||
responseNotice(c, NOTICE_NOTICE, err.Error(), "")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
responseNotice(c, NOTICE_SUCCESS, "删除成功", "")
|
|
||||||
}
|
|
||||||
|
|
||||||
// 添加专题,如果专题有提交 mid 即更新专题
|
|
||||||
func apiSerieAdd(c *gin.Context) {
|
|
||||||
name := c.PostForm("name")
|
|
||||||
slug := c.PostForm("slug")
|
|
||||||
desc := c.PostForm("description")
|
|
||||||
if name == "" || slug == "" || desc == "" {
|
|
||||||
responseNotice(c, NOTICE_NOTICE, "参数错误", "")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
mid, err := strconv.Atoi(c.PostForm("mid"))
|
|
||||||
if err == nil && mid > 0 {
|
|
||||||
serie := QuerySerie(int32(mid))
|
|
||||||
if serie == nil {
|
|
||||||
responseNotice(c, NOTICE_NOTICE, "专题不存在", "")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
serie.Name = name
|
|
||||||
serie.Slug = slug
|
|
||||||
serie.Desc = desc
|
|
||||||
serie.ID = int32(mid)
|
|
||||||
err = UpdateSerie(serie)
|
|
||||||
if err != nil {
|
|
||||||
logd.Error(err)
|
|
||||||
responseNotice(c, NOTICE_NOTICE, err.Error(), "")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
err = AddSerie(name, slug, desc)
|
|
||||||
if err != nil {
|
|
||||||
logd.Error(err)
|
|
||||||
responseNotice(c, NOTICE_NOTICE, err.Error(), "")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
responseNotice(c, NOTICE_SUCCESS, "操作成功", "")
|
|
||||||
}
|
|
||||||
|
|
||||||
// NOTE 排序专题,暂未实现
|
|
||||||
func apiSerieSort(c *gin.Context) {
|
|
||||||
v := c.PostFormArray("mid[]")
|
|
||||||
logd.Debug(v)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 删除草稿箱,物理删除
|
|
||||||
func apiDraftDelete(c *gin.Context) {
|
|
||||||
for _, v := range c.PostFormArray("mid[]") {
|
|
||||||
i, err := strconv.Atoi(v)
|
|
||||||
if err != nil || i < 1 {
|
|
||||||
responseNotice(c, NOTICE_NOTICE, "参数错误", "")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
err = RemoveArticle(int32(i))
|
|
||||||
if err != nil {
|
|
||||||
responseNotice(c, NOTICE_NOTICE, err.Error(), "")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
responseNotice(c, NOTICE_SUCCESS, "删除成功", "")
|
|
||||||
}
|
|
||||||
|
|
||||||
// 删除垃圾箱,物理删除
|
|
||||||
func apiTrashDelete(c *gin.Context) {
|
|
||||||
for _, v := range c.PostFormArray("mid[]") {
|
|
||||||
i, err := strconv.Atoi(v)
|
|
||||||
if err != nil || i < 1 {
|
|
||||||
responseNotice(c, NOTICE_NOTICE, "参数错误", "")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
err = RemoveArticle(int32(i))
|
|
||||||
if err != nil {
|
|
||||||
responseNotice(c, NOTICE_NOTICE, err.Error(), "")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
responseNotice(c, NOTICE_SUCCESS, "删除成功", "")
|
|
||||||
}
|
|
||||||
|
|
||||||
// 从垃圾箱恢复到草稿箱
|
|
||||||
func apiTrashRecover(c *gin.Context) {
|
|
||||||
for _, v := range c.PostFormArray("mid[]") {
|
|
||||||
i, err := strconv.Atoi(v)
|
|
||||||
if err != nil || i < 1 {
|
|
||||||
responseNotice(c, NOTICE_NOTICE, "参数错误", "")
|
|
||||||
return
|
|
||||||
|
|
||||||
}
|
|
||||||
err = RecoverArticle(int32(i))
|
|
||||||
if err != nil {
|
|
||||||
responseNotice(c, NOTICE_NOTICE, err.Error(), "")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
responseNotice(c, NOTICE_SUCCESS, "恢复成功", "")
|
|
||||||
}
|
|
||||||
|
|
||||||
// 上传文件到 qiniu 云
|
|
||||||
func apiFileUpload(c *gin.Context) {
|
|
||||||
type Size interface {
|
|
||||||
Size() int64
|
|
||||||
}
|
|
||||||
file, header, err := c.Request.FormFile("file")
|
|
||||||
if err != nil {
|
|
||||||
logd.Error(err)
|
|
||||||
c.String(http.StatusBadRequest, err.Error())
|
|
||||||
return
|
|
||||||
}
|
|
||||||
s, ok := file.(Size)
|
|
||||||
if !ok {
|
|
||||||
logd.Error("assert failed")
|
|
||||||
c.String(http.StatusBadRequest, "false")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
filename := strings.ToLower(header.Filename)
|
|
||||||
url, err := FileUpload(filename, s.Size(), file)
|
|
||||||
if err != nil {
|
|
||||||
logd.Error(err)
|
|
||||||
c.String(http.StatusBadRequest, err.Error())
|
|
||||||
return
|
|
||||||
}
|
|
||||||
typ := header.Header.Get("Content-Type")
|
|
||||||
c.JSON(http.StatusOK, gin.H{
|
|
||||||
"title": filename,
|
|
||||||
"isImage": typ[:5] == "image",
|
|
||||||
"url": url,
|
|
||||||
"bytes": fmt.Sprintf("%dkb", s.Size()/1000),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// 删除七牛 CDN 文件
|
|
||||||
func apiFileDelete(c *gin.Context) {
|
|
||||||
defer c.String(http.StatusOK, "删掉了吗?鬼知道。。。")
|
|
||||||
|
|
||||||
name := c.PostForm("title")
|
|
||||||
if name == "" {
|
|
||||||
logd.Error("参数错误")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
err := FileDelete(name)
|
|
||||||
if err != nil {
|
|
||||||
logd.Error(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func responseNotice(c *gin.Context, typ, content, hl string) {
|
|
||||||
if hl != "" {
|
|
||||||
c.SetCookie("notice_highlight", hl, 86400, "/", "", true, false)
|
|
||||||
}
|
|
||||||
c.SetCookie("notice_type", typ, 86400, "/", "", true, false)
|
|
||||||
c.SetCookie("notice", fmt.Sprintf("[\"%s\"]", content), 86400, "/", "", true, false)
|
|
||||||
c.Redirect(http.StatusFound, c.Request.Referer())
|
|
||||||
}
|
|
||||||
1
assets/README.md
Normal file
@@ -0,0 +1 @@
|
|||||||
|
Other assets to go along with your repository (images, logos, etc).
|
||||||
|
Before Width: | Height: | Size: 76 KiB After Width: | Height: | Size: 76 KiB |
|
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 14 KiB |
|
Before Width: | Height: | Size: 37 KiB After Width: | Height: | Size: 37 KiB |
|
Before Width: | Height: | Size: 63 KiB After Width: | Height: | Size: 63 KiB |
|
Before Width: | Height: | Size: 44 KiB After Width: | Height: | Size: 44 KiB |
|
Before Width: | Height: | Size: 92 KiB After Width: | Height: | Size: 92 KiB |
|
Before Width: | Height: | Size: 22 KiB After Width: | Height: | Size: 22 KiB |
|
Before Width: | Height: | Size: 847 B After Width: | Height: | Size: 847 B |
|
Before Width: | Height: | Size: 2.5 KiB After Width: | Height: | Size: 2.5 KiB |
|
Before Width: | Height: | Size: 6.3 KiB After Width: | Height: | Size: 6.3 KiB |
|
Before Width: | Height: | Size: 4.7 KiB After Width: | Height: | Size: 4.7 KiB |
|
Before Width: | Height: | Size: 2.0 KiB After Width: | Height: | Size: 2.0 KiB |
|
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 1.2 KiB |
|
Before Width: | Height: | Size: 4.2 KiB After Width: | Height: | Size: 4.2 KiB |
307
back.go
@@ -1,307 +0,0 @@
|
|||||||
// Package main provides ...
|
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
|
||||||
"html/template"
|
|
||||||
"net/http"
|
|
||||||
"strconv"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/eiblog/eiblog/setting"
|
|
||||||
"github.com/eiblog/utils/logd"
|
|
||||||
"github.com/eiblog/utils/mgo"
|
|
||||||
"github.com/gin-gonic/contrib/sessions"
|
|
||||||
"github.com/gin-gonic/gin"
|
|
||||||
)
|
|
||||||
|
|
||||||
// 是否登录
|
|
||||||
func isLogin(c *gin.Context) bool {
|
|
||||||
session := sessions.Default(c)
|
|
||||||
v := session.Get("username")
|
|
||||||
if v == nil || v.(string) != Ei.Username {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
// 登陆过滤
|
|
||||||
func AuthFilter() gin.HandlerFunc {
|
|
||||||
return func(c *gin.Context) {
|
|
||||||
if !isLogin(c) {
|
|
||||||
c.Abort()
|
|
||||||
c.Redirect(http.StatusFound, "/admin/login")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
c.Next()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 登录界面
|
|
||||||
func HandleLogin(c *gin.Context) {
|
|
||||||
logout := c.Query("logout")
|
|
||||||
if logout == "true" {
|
|
||||||
session := sessions.Default(c)
|
|
||||||
session.Delete("username")
|
|
||||||
session.Save()
|
|
||||||
} else if isLogin(c) {
|
|
||||||
c.Redirect(http.StatusFound, "/admin/profile")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
c.Status(http.StatusOK)
|
|
||||||
RenderHTMLBack(c, "login.html", gin.H{"BTitle": Ei.BTitle})
|
|
||||||
}
|
|
||||||
|
|
||||||
// 登陆接口
|
|
||||||
func HandleLoginPost(c *gin.Context) {
|
|
||||||
user := c.PostForm("user")
|
|
||||||
pwd := c.PostForm("password")
|
|
||||||
// code := c.PostForm("code") // 二次验证
|
|
||||||
if user == "" || pwd == "" {
|
|
||||||
logd.Print("参数错误", user, pwd)
|
|
||||||
c.Redirect(http.StatusFound, "/admin/login")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if Ei.Username != user || !VerifyPasswd(Ei.Password, user, pwd) {
|
|
||||||
logd.Printf("账号或密码错误 %s, %s\n", user, pwd)
|
|
||||||
c.Redirect(http.StatusFound, "/admin/login")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
session := sessions.Default(c)
|
|
||||||
session.Set("username", user)
|
|
||||||
session.Save()
|
|
||||||
Ei.LoginIP = c.ClientIP()
|
|
||||||
Ei.LoginTime = time.Now()
|
|
||||||
UpdateAccountField(mgo.M{"$set": mgo.M{"loginip": Ei.LoginIP, "logintime": Ei.LoginTime}})
|
|
||||||
c.Redirect(http.StatusFound, "/admin/profile")
|
|
||||||
}
|
|
||||||
|
|
||||||
func GetBack() gin.H {
|
|
||||||
return gin.H{"Author": Ei.Username, "Qiniu": setting.Conf.Qiniu}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 个人配置
|
|
||||||
func HandleProfile(c *gin.Context) {
|
|
||||||
h := GetBack()
|
|
||||||
h["Console"] = true
|
|
||||||
h["Path"] = c.Request.URL.Path
|
|
||||||
h["Title"] = "个人配置 | " + Ei.BTitle
|
|
||||||
h["Account"] = Ei
|
|
||||||
c.Status(http.StatusOK)
|
|
||||||
RenderHTMLBack(c, "admin-profile", h)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 写文章==>Write
|
|
||||||
type T struct {
|
|
||||||
ID string `json:"id"`
|
|
||||||
Tags string `json:"tags"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func HandlePost(c *gin.Context) {
|
|
||||||
h := GetBack()
|
|
||||||
id, err := strconv.Atoi(c.Query("cid"))
|
|
||||||
if err == nil && id > 0 {
|
|
||||||
artc := QueryArticle(int32(id))
|
|
||||||
if artc != nil {
|
|
||||||
h["Title"] = "编辑文章 | " + Ei.BTitle
|
|
||||||
h["Edit"] = artc
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if h["Title"] == nil {
|
|
||||||
h["Title"] = "撰写文章 | " + Ei.BTitle
|
|
||||||
}
|
|
||||||
h["Path"] = c.Request.URL.Path
|
|
||||||
h["Domain"] = setting.Conf.Mode.Domain
|
|
||||||
h["Series"] = Ei.Series
|
|
||||||
var tags []T
|
|
||||||
for tag, _ := range Ei.Tags {
|
|
||||||
tags = append(tags, T{tag, tag})
|
|
||||||
}
|
|
||||||
str, _ := json.Marshal(tags)
|
|
||||||
h["Tags"] = string(str)
|
|
||||||
c.Status(http.StatusOK)
|
|
||||||
RenderHTMLBack(c, "admin-post", h)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 删除草稿
|
|
||||||
func HandleDraftDelete(c *gin.Context) {
|
|
||||||
id, err := strconv.Atoi(c.Query("cid"))
|
|
||||||
if err != nil || id < 1 {
|
|
||||||
c.JSON(http.StatusBadRequest, gin.H{"error": "参数错误"})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if err = RemoveArticle(int32(id)); err != nil {
|
|
||||||
c.JSON(http.StatusBadRequest, gin.H{"error": "删除错误"})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
c.Redirect(http.StatusFound, "/admin/write-post")
|
|
||||||
}
|
|
||||||
|
|
||||||
// 文章管理==>Manage
|
|
||||||
func HandlePosts(c *gin.Context) {
|
|
||||||
kw := c.Query("keywords")
|
|
||||||
tmp := c.Query("serie")
|
|
||||||
se, err := strconv.Atoi(tmp)
|
|
||||||
if err != nil || se < 1 {
|
|
||||||
se = 0
|
|
||||||
}
|
|
||||||
pg, err := strconv.Atoi(c.Query("page"))
|
|
||||||
if err != nil || pg < 1 {
|
|
||||||
pg = 1
|
|
||||||
}
|
|
||||||
vals := c.Request.URL.Query()
|
|
||||||
h := GetBack()
|
|
||||||
h["Manage"] = true
|
|
||||||
h["Path"] = c.Request.URL.Path
|
|
||||||
h["Title"] = "文章管理 | " + Ei.BTitle
|
|
||||||
h["Series"] = Ei.Series
|
|
||||||
h["Serie"] = se
|
|
||||||
h["KW"] = kw
|
|
||||||
var max int
|
|
||||||
max, h["List"] = PageListBack(se, kw, false, false, pg, setting.Conf.General.PageSize)
|
|
||||||
if pg < max {
|
|
||||||
vals.Set("page", fmt.Sprint(pg+1))
|
|
||||||
h["Next"] = vals.Encode()
|
|
||||||
}
|
|
||||||
if pg > 1 {
|
|
||||||
vals.Set("page", fmt.Sprint(pg-1))
|
|
||||||
h["Prev"] = vals.Encode()
|
|
||||||
}
|
|
||||||
h["PP"] = make(map[int]string, max)
|
|
||||||
for i := 0; i < max; i++ {
|
|
||||||
vals.Set("page", fmt.Sprint(i+1))
|
|
||||||
h["PP"].(map[int]string)[i+1] = vals.Encode()
|
|
||||||
}
|
|
||||||
h["Cur"] = pg
|
|
||||||
c.Status(http.StatusOK)
|
|
||||||
RenderHTMLBack(c, "admin-posts", h)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 专题列表
|
|
||||||
func HandleSeries(c *gin.Context) {
|
|
||||||
h := GetBack()
|
|
||||||
h["Manage"] = true
|
|
||||||
h["Path"] = c.Request.URL.Path
|
|
||||||
h["Title"] = "专题管理 | " + Ei.BTitle
|
|
||||||
h["List"] = Ei.Series
|
|
||||||
c.Status(http.StatusOK)
|
|
||||||
RenderHTMLBack(c, "admin-series", h)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 编辑专题
|
|
||||||
func HandleSerie(c *gin.Context) {
|
|
||||||
h := GetBack()
|
|
||||||
id, err := strconv.Atoi(c.Query("mid"))
|
|
||||||
if serie := QuerySerie(int32(id)); err == nil && id > 0 && serie != nil {
|
|
||||||
h["Title"] = "编辑专题 | " + Ei.BTitle
|
|
||||||
h["Edit"] = serie
|
|
||||||
} else {
|
|
||||||
h["Title"] = "新增专题 | " + Ei.BTitle
|
|
||||||
}
|
|
||||||
h["Manage"] = true
|
|
||||||
h["Path"] = c.Request.URL.Path
|
|
||||||
c.Status(http.StatusOK)
|
|
||||||
RenderHTMLBack(c, "admin-serie", h)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 标签列表
|
|
||||||
func HandleTags(c *gin.Context) {
|
|
||||||
h := GetBack()
|
|
||||||
h["Manage"] = true
|
|
||||||
h["Path"] = c.Request.URL.Path
|
|
||||||
h["Title"] = "标签管理 | " + Ei.BTitle
|
|
||||||
h["List"] = Ei.Tags
|
|
||||||
c.Status(http.StatusOK)
|
|
||||||
RenderHTMLBack(c, "admin-tags", h)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 草稿箱
|
|
||||||
func HandleDraft(c *gin.Context) {
|
|
||||||
h := GetBack()
|
|
||||||
h["Manage"] = true
|
|
||||||
h["Path"] = c.Request.URL.Path
|
|
||||||
h["Title"] = "草稿箱 | " + Ei.BTitle
|
|
||||||
var err error
|
|
||||||
h["List"], err = LoadDraft()
|
|
||||||
if err != nil {
|
|
||||||
logd.Error(err)
|
|
||||||
c.Status(http.StatusBadRequest)
|
|
||||||
} else {
|
|
||||||
c.Status(http.StatusOK)
|
|
||||||
}
|
|
||||||
RenderHTMLBack(c, "admin-draft", h)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 回收箱
|
|
||||||
func HandleTrash(c *gin.Context) {
|
|
||||||
h := GetBack()
|
|
||||||
h["Manage"] = true
|
|
||||||
h["Path"] = c.Request.URL.Path
|
|
||||||
h["Title"] = "回收箱 | " + Ei.BTitle
|
|
||||||
var err error
|
|
||||||
h["List"], err = LoadTrash()
|
|
||||||
if err != nil {
|
|
||||||
logd.Error(err)
|
|
||||||
c.HTML(http.StatusBadRequest, "backLayout.html", h)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
c.Status(http.StatusOK)
|
|
||||||
RenderHTMLBack(c, "admin-trash", h)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 基本设置==>Setting
|
|
||||||
func HandleGeneral(c *gin.Context) {
|
|
||||||
h := GetBack()
|
|
||||||
h["Setting"] = true
|
|
||||||
h["Path"] = c.Request.URL.Path
|
|
||||||
h["Title"] = "基本设置 | " + Ei.BTitle
|
|
||||||
c.Status(http.StatusOK)
|
|
||||||
RenderHTMLBack(c, "admin-general", h)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 阅读设置
|
|
||||||
func HandleDiscussion(c *gin.Context) {
|
|
||||||
h := GetBack()
|
|
||||||
h["Setting"] = true
|
|
||||||
h["Path"] = c.Request.URL.Path
|
|
||||||
h["Title"] = "阅读设置 | " + Ei.BTitle
|
|
||||||
c.Status(http.StatusOK)
|
|
||||||
RenderHTMLBack(c, "admin-discussion", h)
|
|
||||||
}
|
|
||||||
|
|
||||||
// api
|
|
||||||
func HandleAPI(c *gin.Context) {
|
|
||||||
action := c.Param("action")
|
|
||||||
logd.Debug("action=======>", action)
|
|
||||||
api := APIs[action]
|
|
||||||
if api == nil {
|
|
||||||
c.JSON(http.StatusNotFound, gin.H{"error": "Invalid API Request"})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
api(c)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 渲染 html
|
|
||||||
func RenderHTMLBack(c *gin.Context, name string, data gin.H) {
|
|
||||||
if name == "login.html" {
|
|
||||||
err := Tmpl.ExecuteTemplate(c.Writer, name, data)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
c.Header("Content-Type", "text/html; charset=utf-8")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
var buf bytes.Buffer
|
|
||||||
err := Tmpl.ExecuteTemplate(&buf, name, data)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
data["LayoutContent"] = template.HTML(buf.String())
|
|
||||||
err = Tmpl.ExecuteTemplate(c.Writer, "backLayout.html", data)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
c.Header("Content-Type", "text/html; charset=utf-8")
|
|
||||||
}
|
|
||||||
5
build/README.md
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
Packaging and Continuous Integration.
|
||||||
|
|
||||||
|
Put your cloud (AMI), container (Docker), OS (deb, rpm, pkg) package configurations and scripts in the `/build/package` directory.
|
||||||
|
|
||||||
|
Put your CI (travis, circle, drone) configurations and scripts in the `/build/ci` directory. Note that some of the CI tools (e.g., Travis CI) are very picky about the location of their config files. Try putting the config files in the `/build/ci` directory linking them to the location where the CI tools expect them (when possible).
|
||||||
14
build/package/backup.Dockerfile
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
FROM alpine:latest
|
||||||
|
LABEL maintainer="deepzz.qi@gmail.com"
|
||||||
|
|
||||||
|
COPY README.md /app/README.md
|
||||||
|
COPY CHANGELOG.md /app/CHANGELOG.md
|
||||||
|
COPY LICENSE /app/LICENSE
|
||||||
|
|
||||||
|
COPY bin/backend /app/backend
|
||||||
|
COPY conf /app/conf
|
||||||
|
|
||||||
|
EXPOSE 9000
|
||||||
|
|
||||||
|
WORKDIR /app
|
||||||
|
CMD ["backend"]
|
||||||
14
build/package/blog.Dockerfile
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
FROM alpine:latest
|
||||||
|
LABEL maintainer="deepzz.qi@gmail.com"
|
||||||
|
|
||||||
|
COPY README.md /app/README.md
|
||||||
|
COPY CHANGELOG.md /app/CHANGELOG.md
|
||||||
|
COPY LICENSE /app/LICENSE
|
||||||
|
|
||||||
|
COPY bin/backend /app/backend
|
||||||
|
COPY conf /app/conf
|
||||||
|
|
||||||
|
EXPOSE 9000
|
||||||
|
|
||||||
|
WORKDIR /app
|
||||||
|
CMD ["backend"]
|
||||||
51
check.go
@@ -1,51 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"regexp"
|
|
||||||
"strconv"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
// 检查 email
|
|
||||||
func CheckEmail(e string) bool {
|
|
||||||
reg := regexp.MustCompile(`^(\w)+([\.\-]\w+)*@(\w)+((\.\w+)+)$`)
|
|
||||||
return reg.MatchString(e)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 检查 domain
|
|
||||||
func CheckDomain(domain string) bool {
|
|
||||||
reg := regexp.MustCompile(`^(http://|https://)?[0-9a-zA-Z]+[0-9a-zA-Z\.-]*\.[a-zA-Z]{2,4}$`)
|
|
||||||
return reg.MatchString(domain)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 检查 sms
|
|
||||||
func CheckSMS(sms string) bool {
|
|
||||||
reg := regexp.MustCompile(`^\+\d+$`)
|
|
||||||
return reg.MatchString(sms)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 检查 password
|
|
||||||
func CheckPwd(pwd string) bool {
|
|
||||||
return len(pwd) > 5 && len(pwd) < 19
|
|
||||||
}
|
|
||||||
|
|
||||||
// 检查日期
|
|
||||||
func CheckDate(date string) time.Time {
|
|
||||||
if t, err := time.ParseInLocation("2006-01-02 15:04", date, time.Local); err == nil {
|
|
||||||
return t
|
|
||||||
}
|
|
||||||
return time.Now()
|
|
||||||
}
|
|
||||||
|
|
||||||
// 检查 id
|
|
||||||
func CheckSerieID(sid string) int32 {
|
|
||||||
if id, err := strconv.Atoi(sid); err == nil {
|
|
||||||
return int32(id)
|
|
||||||
}
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
// bool
|
|
||||||
func CheckBool(str string) bool {
|
|
||||||
return str == "true" || str == "1"
|
|
||||||
}
|
|
||||||
@@ -1,41 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestCheckEmail(t *testing.T) {
|
|
||||||
emails := []string{
|
|
||||||
"xx@email.com",
|
|
||||||
"xxxxemail.com",
|
|
||||||
"xxx#email.com",
|
|
||||||
}
|
|
||||||
|
|
||||||
for i, v := range emails {
|
|
||||||
if i == 0 {
|
|
||||||
assert.True(t, CheckEmail(v))
|
|
||||||
} else {
|
|
||||||
assert.False(t, CheckEmail(v))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestCheckDomain(t *testing.T) {
|
|
||||||
domains := []string{
|
|
||||||
"123.com",
|
|
||||||
"http://123.com",
|
|
||||||
"https://123.com",
|
|
||||||
"123#.com",
|
|
||||||
"123.coooom",
|
|
||||||
}
|
|
||||||
|
|
||||||
for i, v := range domains {
|
|
||||||
if i > 2 {
|
|
||||||
assert.False(t, CheckDomain(v))
|
|
||||||
} else {
|
|
||||||
assert.True(t, CheckDomain(v))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
7
cmd/README.md
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
Main applications for this project.
|
||||||
|
|
||||||
|
The directory name for each application should match the name of the executable you want to have (e.g., `/cmd/myapp`).
|
||||||
|
|
||||||
|
Don't put a lot of code in the application directory. If you think the code can be imported and used in other projects, then it should live in the `/pkg` directory. If the code is not reusable or if you don't want others to reuse it, put that code in the `/internal` directory. You'll be surprised what others will do, so be explicit about your intentions!
|
||||||
|
|
||||||
|
It's common to have a small `main` function that imports and invokes the code from the `/internal` and `/pkg` directories and nothing else.
|
||||||
61
cmd/blog/main.go
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
// Package main provides ...
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"path/filepath"
|
||||||
|
|
||||||
|
"github.com/eiblog/eiblog/v2/pkg/config"
|
||||||
|
"github.com/eiblog/eiblog/v2/pkg/core/blog/file"
|
||||||
|
"github.com/eiblog/eiblog/v2/pkg/core/blog/page"
|
||||||
|
"github.com/eiblog/eiblog/v2/pkg/core/blog/swag"
|
||||||
|
"github.com/eiblog/eiblog/v2/pkg/mid"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
fmt.Println("Hi, it's App Demo")
|
||||||
|
|
||||||
|
endRun := make(chan bool, 1)
|
||||||
|
|
||||||
|
runHTTPServer(endRun)
|
||||||
|
<-endRun
|
||||||
|
}
|
||||||
|
|
||||||
|
func runHTTPServer(endRun chan bool) {
|
||||||
|
if !config.Conf.BlogApp.EnableHTTP {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if config.Conf.RunMode == config.ModeProd {
|
||||||
|
gin.SetMode(gin.ReleaseMode)
|
||||||
|
}
|
||||||
|
e := gin.Default()
|
||||||
|
// middleware
|
||||||
|
e.Use(mid.UserMiddleware())
|
||||||
|
e.Use(mid.SessionMiddleware(mid.SessionOpts{
|
||||||
|
Name: "su",
|
||||||
|
Secure: config.Conf.RunMode == config.ModeProd,
|
||||||
|
Secret: []byte("ZGlzvcmUoMTAsICI="),
|
||||||
|
}))
|
||||||
|
|
||||||
|
// swag
|
||||||
|
swag.RegisterRoutes(e)
|
||||||
|
|
||||||
|
// static files, page
|
||||||
|
root := filepath.Join(config.WorkDir, "assets")
|
||||||
|
e.Static("/static", root)
|
||||||
|
|
||||||
|
// frontend pages
|
||||||
|
page.RegisterRoutes(e)
|
||||||
|
// static files
|
||||||
|
file.RegisterRoutes(e)
|
||||||
|
|
||||||
|
// api router
|
||||||
|
|
||||||
|
// start
|
||||||
|
address := fmt.Sprintf(":%d", config.Conf.BlogApp.HTTPPort)
|
||||||
|
go e.Run(address)
|
||||||
|
fmt.Println("HTTP server running on: " + address)
|
||||||
|
}
|
||||||
3
conf/README.md
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
Configuration file templates or default configs.
|
||||||
|
|
||||||
|
Put your confd or consul-template template files here.
|
||||||
123
conf/app.yml
@@ -1,101 +1,68 @@
|
|||||||
# 运行模式 dev or prod
|
appname: eiblog
|
||||||
runmode: dev
|
database:
|
||||||
# 静态文件版本
|
driver: postgres
|
||||||
staticversion: 1
|
source: host=127.0.0.1 port=5432 user=postgres dbname=example sslmode=disable password=MTI3LjAuMC4x
|
||||||
# superfeedr url
|
eshost: http://localhost:9200
|
||||||
feedrurl: https://deepzz.superfeedr.com/
|
blogapp:
|
||||||
# 热搜词配置
|
mode:
|
||||||
hotwords:
|
name: cmd-blog
|
||||||
|
enablehttp: true
|
||||||
|
httpport: 9000
|
||||||
|
host: example.com
|
||||||
|
staticversion: 1 # 静态文件版本
|
||||||
|
hotwords: # 热搜词
|
||||||
- docker
|
- docker
|
||||||
- mongodb
|
- mongodb
|
||||||
- curl
|
- curl
|
||||||
- dns
|
- dns
|
||||||
# ping rpcs 地址
|
general: # 常规配置
|
||||||
pingrpcs:
|
pagenum: 10 # 首页展示文章数量
|
||||||
- http://ping.baidu.com/ping/RPC2
|
pagesize: 20 # 管理界面
|
||||||
- http://blogsearch.google.com/ping/RPC2
|
startid: 11 # 起始ID,预留id不时之需, 不用管
|
||||||
- http://rpc.pingomatic.com/
|
descprefix: "Desc:" # 文章描述前缀
|
||||||
# 常规配置
|
identifier: <!--more--> # 截取预览标识
|
||||||
general:
|
length: 400 # 自动截取预览, 字符数
|
||||||
# 首页展示文章数量
|
trash: -48 # 回收箱保留48小时
|
||||||
pagenum: 10
|
clean: 1 # 定时清理回收箱,每 %d 小时
|
||||||
# 管理界面
|
disqus: # 评论相关
|
||||||
pagesize: 20
|
|
||||||
# 起始ID,预留id不时之需, 不用管
|
|
||||||
startid: 11
|
|
||||||
# 文章描述前缀
|
|
||||||
descprefix: "Desc:"
|
|
||||||
# 截取预览标识
|
|
||||||
identifier: <!--more-->
|
|
||||||
# 自动截取预览, 字符数
|
|
||||||
length: 400
|
|
||||||
# 回收箱保留48小时
|
|
||||||
trash: -48
|
|
||||||
# 定时清理回收箱,每 %d 小时
|
|
||||||
clean: 1
|
|
||||||
# 评论相关
|
|
||||||
disqus:
|
|
||||||
shortname: xxxxxx
|
shortname: xxxxxx
|
||||||
publickey: wdSgxRm9rdGAlLKFcFdToBe3GT4SibmV7Y8EjJQ0r4GWXeKtxpopMAeIeoI2dTEg
|
publickey: wdSgxRm9rdGAlLKFcFdToBe3GT4SibmV7Y8EjJQ0r4GWXeKtxpopMAeIeoI2dTEg
|
||||||
accesstoken: 50023908f39f4607957e909b495326af
|
accesstoken: 50023908f39f4607957e909b495326af
|
||||||
postscount: https://disqus.com/api/3.0/threads/set.json
|
|
||||||
postslist: https://disqus.com/api/3.0/threads/listPosts.json
|
|
||||||
postcreate: https://disqus.com/api/3.0/posts/create.json
|
|
||||||
postapprove: https://disqus.com/api/3.0/posts/approve.json
|
|
||||||
threadcreate: https://disqus.com/api/3.0/threads/create.json
|
|
||||||
# 获取评论数量间隔
|
|
||||||
interval: 5
|
|
||||||
# 谷歌统计
|
|
||||||
google:
|
google:
|
||||||
url: https://www.google-analytics.com/collect
|
url: https://www.google-analytics.com/collect
|
||||||
tid: UA-xxxxxx-1
|
tid: UA-xxxxxx-1
|
||||||
v: "1"
|
v: "1"
|
||||||
t: pageview
|
t: pageview
|
||||||
# 七牛CDN
|
qiniu: # 七牛OSS
|
||||||
qiniu:
|
|
||||||
bucket: 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
|
||||||
# 运行模式
|
twitter: # twitter card
|
||||||
mode:
|
|
||||||
# http server
|
|
||||||
enablehttp: true
|
|
||||||
httpport: 9000
|
|
||||||
# https server
|
|
||||||
enablehttps: false
|
|
||||||
autocert: false
|
|
||||||
httpsport: 9001
|
|
||||||
certfile: conf/ssl/domain.rsa.pem
|
|
||||||
keyfile: conf/ssl/domain.rsa.key
|
|
||||||
domain: deepzz.com
|
|
||||||
# twitter地址: twitter.com/chenqijing2
|
|
||||||
twitter:
|
|
||||||
card: summary
|
card: summary
|
||||||
site: deepzz02
|
site: deepzz02
|
||||||
image: st.deepzz.com/static/img/avatar.jpg
|
image: st.deepzz.com/static/img/avatar.jpg
|
||||||
address: twitter.com/deepzz02
|
address: twitter.com/deepzz02
|
||||||
|
feedrpc:
|
||||||
|
feedrurl: https://deepzz.superfeedr.com/
|
||||||
|
pingrpc:
|
||||||
|
- http://ping.baidu.com/ping/RPC2
|
||||||
|
- http://blogsearch.google.com/ping/RPC2
|
||||||
|
- http://rpc.pingomatic.com/
|
||||||
# 数据初始化操作,可到博客后台修改
|
# 数据初始化操作,可到博客后台修改
|
||||||
account:
|
account:
|
||||||
# *后台登录用户名
|
username: deepzz # *后台登录用户名
|
||||||
username: deepzz
|
password: deepzz # *登录明文密码
|
||||||
# *登录明文密码
|
email: chenqijing2@163.com # 邮箱,用于通知: chenqijing2@163.com
|
||||||
password: deepzz
|
phonenumber: "+8615100000000" # 手机号, "+8615100000000"
|
||||||
# 邮箱,用于通知: chenqijing2@163.com
|
address: "" # 家庭住址
|
||||||
email: chenqijing2@163.com
|
|
||||||
# 手机号, "+8615100000000"
|
|
||||||
phonenumber: "+8615100000000"
|
|
||||||
# 家庭住址
|
|
||||||
address: ""
|
|
||||||
blogger:
|
blogger:
|
||||||
# left显示名称: Deepzz
|
blogname: Deepzz # left显示名称: Deepzz
|
||||||
blogname: Deepzz
|
subtitle: 不抛弃,不放弃 # 小标题: 不抛弃,不放弃
|
||||||
# 小标题: 不抛弃,不放弃
|
beian: 蜀 ICP 备 16021362 号 # 备案号: 蜀 ICP 备 16021362 号
|
||||||
subtitle: 不抛弃,不放弃
|
btitle: Deepzz's Blog # footer显示名称及tab标题: Deepzz's Blog
|
||||||
# 备案号: 蜀 ICP 备 16021362 号
|
copyright: 本站使用「<a href="//creativecommons.org/licenses/by/4.0/">署名 4.0 国际</a>」创作共享协议,转载请注明作者及原网址。 # 版权声明
|
||||||
beian: 蜀 ICP 备 16021362 号
|
backupapp:
|
||||||
# footer显示名称及tab标题: Deepzz's Blog
|
name: cmd-backup
|
||||||
btitle: Deepzz's Blog
|
enablehttp: true
|
||||||
# 版权声明
|
httpport: 9000
|
||||||
copyright: 本站使用「<a href="//creativecommons.org/licenses/by/4.0/">署名 4.0 国际</a>」创作共享协议,转载请注明作者及原网址。
|
|
||||||
|
|||||||
@@ -1 +0,0 @@
|
|||||||
# like 192.168.99.100:true
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
ua,user-agent,userAgent
|
|
||||||
js,javascript
|
|
||||||
谷歌=>google
|
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
network.host: 0.0.0.0
|
|
||||||
|
|
||||||
index:
|
|
||||||
analysis:
|
|
||||||
analyzer:
|
|
||||||
ik_syno:
|
|
||||||
type: custom
|
|
||||||
tokenizer: ik_max_word
|
|
||||||
filter: [my_synonym_filter]
|
|
||||||
ik_syno_smart:
|
|
||||||
type: custom
|
|
||||||
tokenizer: ik_smart
|
|
||||||
filter: [my_synonym_filter]
|
|
||||||
filter:
|
|
||||||
my_synonym_filter:
|
|
||||||
type: synonym
|
|
||||||
synonyms_path: analysis/synonym.txt
|
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
# you can override this using by setting a system property, for example -Des.logger.level=DEBUG
|
|
||||||
es.logger.level: INFO
|
|
||||||
rootLogger: ${es.logger.level}, console
|
|
||||||
logger:
|
|
||||||
# log action execution errors for easier debugging
|
|
||||||
action: DEBUG
|
|
||||||
# reduce the logging for aws, too much is logged under the default INFO
|
|
||||||
com.amazonaws: WARN
|
|
||||||
|
|
||||||
appender:
|
|
||||||
console:
|
|
||||||
type: console
|
|
||||||
layout:
|
|
||||||
type: consolePattern
|
|
||||||
conversionPattern: "[%d{ISO8601}][%-5p][%-25c] %m%n"
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd">
|
|
||||||
<properties>
|
|
||||||
<comment>IK Analyzer 扩展配置</comment>
|
|
||||||
<!--用户可以在这里配置自己的扩展字典 -->
|
|
||||||
<entry key="ext_dict">custom/mydict.dic;custom/single_word_low_freq.dic</entry>
|
|
||||||
<!--用户可以在这里配置自己的扩展停止词字典-->
|
|
||||||
<entry key="ext_stopwords">custom/ext_stopword.dic</entry>
|
|
||||||
<!--用户可以在这里配置远程扩展字典 -->
|
|
||||||
<!-- <entry key="remote_ext_dict">words_location</entry> -->
|
|
||||||
<!--用户可以在这里配置远程扩展停止词字典-->
|
|
||||||
<!-- <entry key="remote_ext_stopwords">words_location</entry> -->
|
|
||||||
</properties>
|
|
||||||
@@ -1,31 +0,0 @@
|
|||||||
也
|
|
||||||
了
|
|
||||||
仍
|
|
||||||
从
|
|
||||||
以
|
|
||||||
使
|
|
||||||
则
|
|
||||||
却
|
|
||||||
又
|
|
||||||
及
|
|
||||||
对
|
|
||||||
就
|
|
||||||
并
|
|
||||||
很
|
|
||||||
或
|
|
||||||
把
|
|
||||||
是
|
|
||||||
的
|
|
||||||
着
|
|
||||||
给
|
|
||||||
而
|
|
||||||
被
|
|
||||||
让
|
|
||||||
在
|
|
||||||
还
|
|
||||||
比
|
|
||||||
等
|
|
||||||
当
|
|
||||||
与
|
|
||||||
于
|
|
||||||
但
|
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
medcl
|
|
||||||
elastic
|
|
||||||
elasticsearch
|
|
||||||
kogstash
|
|
||||||
kibana
|
|
||||||
marvel
|
|
||||||
shield
|
|
||||||
watcher
|
|
||||||
beats
|
|
||||||
packetbeat
|
|
||||||
filebeat
|
|
||||||
topbeat
|
|
||||||
metrixbeat
|
|
||||||
kimchy
|
|
||||||
@@ -1,25 +0,0 @@
|
|||||||
不
|
|
||||||
也
|
|
||||||
了
|
|
||||||
仍
|
|
||||||
从
|
|
||||||
以
|
|
||||||
使
|
|
||||||
则
|
|
||||||
却
|
|
||||||
又
|
|
||||||
及
|
|
||||||
对
|
|
||||||
就
|
|
||||||
并
|
|
||||||
很
|
|
||||||
或
|
|
||||||
把
|
|
||||||
是
|
|
||||||
的
|
|
||||||
着
|
|
||||||
给
|
|
||||||
而
|
|
||||||
被
|
|
||||||
让
|
|
||||||
但
|
|
||||||
@@ -1,316 +0,0 @@
|
|||||||
丈
|
|
||||||
下
|
|
||||||
世
|
|
||||||
世纪
|
|
||||||
两
|
|
||||||
个
|
|
||||||
中
|
|
||||||
串
|
|
||||||
亩
|
|
||||||
人
|
|
||||||
介
|
|
||||||
付
|
|
||||||
代
|
|
||||||
件
|
|
||||||
任
|
|
||||||
份
|
|
||||||
伏
|
|
||||||
伙
|
|
||||||
位
|
|
||||||
位数
|
|
||||||
例
|
|
||||||
倍
|
|
||||||
像素
|
|
||||||
元
|
|
||||||
克
|
|
||||||
克拉
|
|
||||||
公亩
|
|
||||||
公克
|
|
||||||
公分
|
|
||||||
公升
|
|
||||||
公尺
|
|
||||||
公担
|
|
||||||
公斤
|
|
||||||
公里
|
|
||||||
公顷
|
|
||||||
具
|
|
||||||
册
|
|
||||||
出
|
|
||||||
刀
|
|
||||||
分
|
|
||||||
分钟
|
|
||||||
分米
|
|
||||||
划
|
|
||||||
列
|
|
||||||
则
|
|
||||||
刻
|
|
||||||
剂
|
|
||||||
剑
|
|
||||||
副
|
|
||||||
加仑
|
|
||||||
勺
|
|
||||||
包
|
|
||||||
匙
|
|
||||||
匹
|
|
||||||
区
|
|
||||||
千克
|
|
||||||
千米
|
|
||||||
升
|
|
||||||
卷
|
|
||||||
厅
|
|
||||||
厘
|
|
||||||
厘米
|
|
||||||
双
|
|
||||||
发
|
|
||||||
口
|
|
||||||
句
|
|
||||||
只
|
|
||||||
台
|
|
||||||
叶
|
|
||||||
号
|
|
||||||
名
|
|
||||||
吨
|
|
||||||
听
|
|
||||||
员
|
|
||||||
周
|
|
||||||
周年
|
|
||||||
品
|
|
||||||
回
|
|
||||||
团
|
|
||||||
圆
|
|
||||||
圈
|
|
||||||
地
|
|
||||||
场
|
|
||||||
块
|
|
||||||
坪
|
|
||||||
堆
|
|
||||||
声
|
|
||||||
壶
|
|
||||||
处
|
|
||||||
夜
|
|
||||||
大
|
|
||||||
天
|
|
||||||
头
|
|
||||||
套
|
|
||||||
女
|
|
||||||
孔
|
|
||||||
字
|
|
||||||
宗
|
|
||||||
室
|
|
||||||
家
|
|
||||||
寸
|
|
||||||
对
|
|
||||||
封
|
|
||||||
尊
|
|
||||||
小时
|
|
||||||
尺
|
|
||||||
尾
|
|
||||||
局
|
|
||||||
层
|
|
||||||
届
|
|
||||||
岁
|
|
||||||
师
|
|
||||||
帧
|
|
||||||
幅
|
|
||||||
幕
|
|
||||||
幢
|
|
||||||
平方
|
|
||||||
平方公尺
|
|
||||||
平方公里
|
|
||||||
平方分米
|
|
||||||
平方厘米
|
|
||||||
平方码
|
|
||||||
平方米
|
|
||||||
平方英寸
|
|
||||||
平方英尺
|
|
||||||
平方英里
|
|
||||||
平米
|
|
||||||
年
|
|
||||||
年代
|
|
||||||
年级
|
|
||||||
度
|
|
||||||
座
|
|
||||||
式
|
|
||||||
引
|
|
||||||
张
|
|
||||||
成
|
|
||||||
战
|
|
||||||
截
|
|
||||||
户
|
|
||||||
房
|
|
||||||
所
|
|
||||||
扇
|
|
||||||
手
|
|
||||||
打
|
|
||||||
批
|
|
||||||
把
|
|
||||||
折
|
|
||||||
担
|
|
||||||
拍
|
|
||||||
招
|
|
||||||
拨
|
|
||||||
拳
|
|
||||||
指
|
|
||||||
掌
|
|
||||||
排
|
|
||||||
撮
|
|
||||||
支
|
|
||||||
文
|
|
||||||
斗
|
|
||||||
斤
|
|
||||||
方
|
|
||||||
族
|
|
||||||
日
|
|
||||||
时
|
|
||||||
曲
|
|
||||||
月
|
|
||||||
月份
|
|
||||||
期
|
|
||||||
本
|
|
||||||
朵
|
|
||||||
村
|
|
||||||
束
|
|
||||||
条
|
|
||||||
来
|
|
||||||
杯
|
|
||||||
枚
|
|
||||||
枝
|
|
||||||
枪
|
|
||||||
架
|
|
||||||
柄
|
|
||||||
柜
|
|
||||||
栋
|
|
||||||
栏
|
|
||||||
株
|
|
||||||
样
|
|
||||||
根
|
|
||||||
格
|
|
||||||
案
|
|
||||||
桌
|
|
||||||
档
|
|
||||||
桩
|
|
||||||
桶
|
|
||||||
梯
|
|
||||||
棵
|
|
||||||
楼
|
|
||||||
次
|
|
||||||
款
|
|
||||||
步
|
|
||||||
段
|
|
||||||
毛
|
|
||||||
毫
|
|
||||||
毫升
|
|
||||||
毫米
|
|
||||||
毫克
|
|
||||||
池
|
|
||||||
洲
|
|
||||||
派
|
|
||||||
海里
|
|
||||||
滴
|
|
||||||
炮
|
|
||||||
点
|
|
||||||
点钟
|
|
||||||
片
|
|
||||||
版
|
|
||||||
环
|
|
||||||
班
|
|
||||||
瓣
|
|
||||||
瓶
|
|
||||||
生
|
|
||||||
男
|
|
||||||
画
|
|
||||||
界
|
|
||||||
盆
|
|
||||||
盎司
|
|
||||||
盏
|
|
||||||
盒
|
|
||||||
盘
|
|
||||||
相
|
|
||||||
眼
|
|
||||||
石
|
|
||||||
码
|
|
||||||
碗
|
|
||||||
碟
|
|
||||||
磅
|
|
||||||
种
|
|
||||||
科
|
|
||||||
秒
|
|
||||||
秒钟
|
|
||||||
窝
|
|
||||||
立方公尺
|
|
||||||
立方分米
|
|
||||||
立方厘米
|
|
||||||
立方码
|
|
||||||
立方米
|
|
||||||
立方英寸
|
|
||||||
立方英尺
|
|
||||||
站
|
|
||||||
章
|
|
||||||
笔
|
|
||||||
等
|
|
||||||
筐
|
|
||||||
筒
|
|
||||||
箱
|
|
||||||
篇
|
|
||||||
篓
|
|
||||||
篮
|
|
||||||
簇
|
|
||||||
米
|
|
||||||
类
|
|
||||||
粒
|
|
||||||
级
|
|
||||||
组
|
|
||||||
维
|
|
||||||
缕
|
|
||||||
缸
|
|
||||||
罐
|
|
||||||
网
|
|
||||||
群
|
|
||||||
股
|
|
||||||
脚
|
|
||||||
船
|
|
||||||
艇
|
|
||||||
艘
|
|
||||||
色
|
|
||||||
节
|
|
||||||
英亩
|
|
||||||
英寸
|
|
||||||
英尺
|
|
||||||
英里
|
|
||||||
行
|
|
||||||
袋
|
|
||||||
角
|
|
||||||
言
|
|
||||||
课
|
|
||||||
起
|
|
||||||
趟
|
|
||||||
路
|
|
||||||
车
|
|
||||||
转
|
|
||||||
轮
|
|
||||||
辆
|
|
||||||
辈
|
|
||||||
连
|
|
||||||
通
|
|
||||||
遍
|
|
||||||
部
|
|
||||||
里
|
|
||||||
重
|
|
||||||
针
|
|
||||||
钟
|
|
||||||
钱
|
|
||||||
锅
|
|
||||||
门
|
|
||||||
间
|
|
||||||
队
|
|
||||||
阶段
|
|
||||||
隅
|
|
||||||
集
|
|
||||||
页
|
|
||||||
顶
|
|
||||||
顷
|
|
||||||
项
|
|
||||||
顿
|
|
||||||
颗
|
|
||||||
餐
|
|
||||||
首
|
|
||||||
@@ -1,33 +0,0 @@
|
|||||||
a
|
|
||||||
an
|
|
||||||
and
|
|
||||||
are
|
|
||||||
as
|
|
||||||
at
|
|
||||||
be
|
|
||||||
but
|
|
||||||
by
|
|
||||||
for
|
|
||||||
if
|
|
||||||
in
|
|
||||||
into
|
|
||||||
is
|
|
||||||
it
|
|
||||||
no
|
|
||||||
not
|
|
||||||
of
|
|
||||||
on
|
|
||||||
or
|
|
||||||
such
|
|
||||||
that
|
|
||||||
the
|
|
||||||
their
|
|
||||||
then
|
|
||||||
there
|
|
||||||
these
|
|
||||||
they
|
|
||||||
this
|
|
||||||
to
|
|
||||||
was
|
|
||||||
will
|
|
||||||
with
|
|
||||||
@@ -1,37 +0,0 @@
|
|||||||
乡
|
|
||||||
井
|
|
||||||
亭
|
|
||||||
党
|
|
||||||
区
|
|
||||||
厅
|
|
||||||
县
|
|
||||||
园
|
|
||||||
塔
|
|
||||||
家
|
|
||||||
寺
|
|
||||||
局
|
|
||||||
巷
|
|
||||||
市
|
|
||||||
弄
|
|
||||||
所
|
|
||||||
斯基
|
|
||||||
楼
|
|
||||||
江
|
|
||||||
河
|
|
||||||
海
|
|
||||||
湖
|
|
||||||
省
|
|
||||||
维奇
|
|
||||||
署
|
|
||||||
苑
|
|
||||||
街
|
|
||||||
觀
|
|
||||||
观
|
|
||||||
诺夫
|
|
||||||
路
|
|
||||||
部
|
|
||||||
镇
|
|
||||||
阁
|
|
||||||
山
|
|
||||||
子
|
|
||||||
娃
|
|
||||||
@@ -1,131 +0,0 @@
|
|||||||
丁
|
|
||||||
万
|
|
||||||
万俟
|
|
||||||
上官
|
|
||||||
东方
|
|
||||||
乔
|
|
||||||
于
|
|
||||||
令狐
|
|
||||||
仲孙
|
|
||||||
任
|
|
||||||
何
|
|
||||||
余
|
|
||||||
候
|
|
||||||
傅
|
|
||||||
公冶
|
|
||||||
公孙
|
|
||||||
公羊
|
|
||||||
冯
|
|
||||||
刘
|
|
||||||
单
|
|
||||||
单于
|
|
||||||
卢
|
|
||||||
史
|
|
||||||
叶
|
|
||||||
司徒
|
|
||||||
司空
|
|
||||||
司马
|
|
||||||
吕
|
|
||||||
吴
|
|
||||||
周
|
|
||||||
唐
|
|
||||||
夏
|
|
||||||
夏侯
|
|
||||||
太叔
|
|
||||||
姚
|
|
||||||
姜
|
|
||||||
孔
|
|
||||||
孙
|
|
||||||
孟
|
|
||||||
宇文
|
|
||||||
宋
|
|
||||||
宗政
|
|
||||||
尉迟
|
|
||||||
尹
|
|
||||||
崔
|
|
||||||
常
|
|
||||||
康
|
|
||||||
廖
|
|
||||||
张
|
|
||||||
彭
|
|
||||||
徐
|
|
||||||
慕容
|
|
||||||
戴
|
|
||||||
文
|
|
||||||
方
|
|
||||||
易
|
|
||||||
曹
|
|
||||||
曾
|
|
||||||
朱
|
|
||||||
李
|
|
||||||
杜
|
|
||||||
杨
|
|
||||||
林
|
|
||||||
梁
|
|
||||||
欧阳
|
|
||||||
武
|
|
||||||
段
|
|
||||||
毛
|
|
||||||
江
|
|
||||||
汤
|
|
||||||
沈
|
|
||||||
淳于
|
|
||||||
潘
|
|
||||||
澹台
|
|
||||||
濮阳
|
|
||||||
熊
|
|
||||||
王
|
|
||||||
田
|
|
||||||
申屠
|
|
||||||
白
|
|
||||||
皇甫
|
|
||||||
石
|
|
||||||
秦
|
|
||||||
程
|
|
||||||
罗
|
|
||||||
肖
|
|
||||||
胡
|
|
||||||
苏
|
|
||||||
范
|
|
||||||
董
|
|
||||||
蒋
|
|
||||||
薛
|
|
||||||
袁
|
|
||||||
许
|
|
||||||
诸葛
|
|
||||||
谢
|
|
||||||
谭
|
|
||||||
贺
|
|
||||||
贾
|
|
||||||
赖
|
|
||||||
赫连
|
|
||||||
赵
|
|
||||||
轩辕
|
|
||||||
邓
|
|
||||||
邱
|
|
||||||
邵
|
|
||||||
邹
|
|
||||||
郑
|
|
||||||
郝
|
|
||||||
郭
|
|
||||||
金
|
|
||||||
钟
|
|
||||||
钟离
|
|
||||||
钱
|
|
||||||
长孙
|
|
||||||
闻人
|
|
||||||
闾丘
|
|
||||||
阎
|
|
||||||
陆
|
|
||||||
陈
|
|
||||||
雷
|
|
||||||
韩
|
|
||||||
顾
|
|
||||||
马
|
|
||||||
高
|
|
||||||
魏
|
|
||||||
鲜于
|
|
||||||
黄
|
|
||||||
黎
|
|
||||||
龙
|
|
||||||
龚
|
|
||||||
@@ -1,80 +0,0 @@
|
|||||||
# Elasticsearch plugin descriptor file
|
|
||||||
# This file must exist as 'plugin-descriptor.properties' at
|
|
||||||
# the root directory of all plugins.
|
|
||||||
#
|
|
||||||
# A plugin can be 'site', 'jvm', or both.
|
|
||||||
#
|
|
||||||
### example site plugin for "foo":
|
|
||||||
#
|
|
||||||
# foo.zip <-- zip file for the plugin, with this structure:
|
|
||||||
# _site/ <-- the contents that will be served
|
|
||||||
# plugin-descriptor.properties <-- example contents below:
|
|
||||||
#
|
|
||||||
# site=true
|
|
||||||
# description=My cool plugin
|
|
||||||
# version=1.0
|
|
||||||
#
|
|
||||||
### example jvm plugin for "foo"
|
|
||||||
#
|
|
||||||
# foo.zip <-- zip file for the plugin, with this structure:
|
|
||||||
# <arbitrary name1>.jar <-- classes, resources, dependencies
|
|
||||||
# <arbitrary nameN>.jar <-- any number of jars
|
|
||||||
# plugin-descriptor.properties <-- example contents below:
|
|
||||||
#
|
|
||||||
# jvm=true
|
|
||||||
# classname=foo.bar.BazPlugin
|
|
||||||
# description=My cool plugin
|
|
||||||
# version=2.0.0-rc1
|
|
||||||
# elasticsearch.version=2.0
|
|
||||||
# java.version=1.7
|
|
||||||
#
|
|
||||||
### mandatory elements for all plugins:
|
|
||||||
#
|
|
||||||
# 'description': simple summary of the plugin
|
|
||||||
description=IK Analyzer for Elasticsearch
|
|
||||||
#
|
|
||||||
# 'version': plugin's version
|
|
||||||
version=1.10.1
|
|
||||||
#
|
|
||||||
# 'name': the plugin name
|
|
||||||
name=analysis-ik
|
|
||||||
|
|
||||||
### mandatory elements for site plugins:
|
|
||||||
#
|
|
||||||
# 'site': set to true to indicate contents of the _site/
|
|
||||||
# directory in the root of the plugin should be served.
|
|
||||||
site=${elasticsearch.plugin.site}
|
|
||||||
#
|
|
||||||
### mandatory elements for jvm plugins :
|
|
||||||
#
|
|
||||||
# 'jvm': true if the 'classname' class should be loaded
|
|
||||||
# from jar files in the root directory of the plugin.
|
|
||||||
# Note that only jar files in the root directory are
|
|
||||||
# added to the classpath for the plugin! If you need
|
|
||||||
# other resources, package them into a resources jar.
|
|
||||||
jvm=true
|
|
||||||
#
|
|
||||||
# 'classname': the name of the class to load, fully-qualified.
|
|
||||||
classname=org.elasticsearch.plugin.analysis.ik.AnalysisIkPlugin
|
|
||||||
#
|
|
||||||
# 'java.version' version of java the code is built against
|
|
||||||
# use the system property java.specification.version
|
|
||||||
# version string must be a sequence of nonnegative decimal integers
|
|
||||||
# separated by "."'s and may have leading zeros
|
|
||||||
java.version=1.7
|
|
||||||
#
|
|
||||||
# 'elasticsearch.version' version of elasticsearch compiled against
|
|
||||||
# You will have to release a new version of the plugin for each new
|
|
||||||
# elasticsearch release. This version is checked when the plugin
|
|
||||||
# is loaded so Elasticsearch will refuse to start in the presence of
|
|
||||||
# plugins with the incorrect elasticsearch.version.
|
|
||||||
elasticsearch.version=2.4.1
|
|
||||||
#
|
|
||||||
### deprecated elements for jvm plugins :
|
|
||||||
#
|
|
||||||
# 'isolated': true if the plugin should have its own classloader.
|
|
||||||
# passing false is deprecated, and only intended to support plugins
|
|
||||||
# that have hard dependencies against each other. If this is
|
|
||||||
# not specified, then the plugin is isolated by default.
|
|
||||||
isolated=${elasticsearch.plugin.isolated}
|
|
||||||
#
|
|
||||||
@@ -1,138 +0,0 @@
|
|||||||
server {
|
|
||||||
listen 443 ssl http2 fastopen=3 reuseport;
|
|
||||||
|
|
||||||
server_name www.deepzz.com deepzz.com;
|
|
||||||
server_tokens off;
|
|
||||||
|
|
||||||
access_log /data/eiblog/logdata/nginx.log;
|
|
||||||
|
|
||||||
# IP黑名单.
|
|
||||||
include /data/eiblog/conf/nginx/ip.blacklist;
|
|
||||||
|
|
||||||
# letsencrypt v2已内置, 忽略.
|
|
||||||
# https://imququ.com/post/certificate-transparency.html#toc-2
|
|
||||||
#ssl_ct on;
|
|
||||||
#ssl_ct_static_scts /data/eiblog/conf/scts/rsa/;
|
|
||||||
#ssl_ct_static_scts /data/eiblog/conf/scts/ecc/;
|
|
||||||
|
|
||||||
# 中间证书 + 根证书.
|
|
||||||
# https://imququ.com/post/why-can-not-turn-on-ocsp-stapling.html
|
|
||||||
ssl_trusted_certificate /data/eiblog/conf/ssl/full_chained.pem;
|
|
||||||
|
|
||||||
# 站点证书 + 中间证书, 私钥.
|
|
||||||
ssl_certificate /data/eiblog/conf/ssl/domain.rsa.pem;
|
|
||||||
ssl_certificate_key /data/eiblog/conf/ssl/domain.rsa.key;
|
|
||||||
# ssl_certificate /data/eiblog/conf/ssl/domain.ecc.pem;
|
|
||||||
# ssl_certificate_key /data/eiblog/conf/ssl/domain.ecc.key;
|
|
||||||
|
|
||||||
# openssl dhparam -out dhparams.pem 2048
|
|
||||||
# https://weakdh.org/sysadmin.html
|
|
||||||
ssl_dhparam /data/eiblog/conf/ssl/dhparams.pem;
|
|
||||||
|
|
||||||
# 单机部署可以不指定.
|
|
||||||
# openssl rand 48 > session_ticket.key
|
|
||||||
# ssl_session_ticket_key /data/eiblog/conf/ssl/session_ticket.key;
|
|
||||||
|
|
||||||
ssl_prefer_server_ciphers on;
|
|
||||||
# https://github.com/cloudflare/sslconfig/blob/master/conf
|
|
||||||
ssl_ciphers EECDH+CHACHA20:EECDH+CHACHA20-draft:EECDH+AES128:RSA+AES128:EECDH+AES256:RSA+AES256:EECDH+3DES:RSA+3DES:!MD5;
|
|
||||||
|
|
||||||
# 如果启用 RSA + ECDSA 双证书, Cipher Suite 可以参考以下配置.
|
|
||||||
# ssl_ciphers EECDH+CHACHA20:EECDH+CHACHA20-draft:EECDH+ECDSA+AES128:EECDH+aRSA+AES128:RSA+AES128:EECDH+ECDSA+AES256:EECDH+aRSA+AES256:RSA+AES256:EECDH+ECDSA+3DES:EECDH+aRSA+3DES:RSA+3DES:!MD5;
|
|
||||||
|
|
||||||
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
|
|
||||||
ssl_session_cache shared:SSL:50m;
|
|
||||||
ssl_session_timeout 1d;
|
|
||||||
ssl_session_tickets on;
|
|
||||||
|
|
||||||
# ssl stapling
|
|
||||||
ssl_stapling on;
|
|
||||||
ssl_stapling_verify on;
|
|
||||||
|
|
||||||
resolver 114.114.114.114 8.8.8.8 valid=300s;
|
|
||||||
resolver_timeout 10s;
|
|
||||||
|
|
||||||
if ($request_method !~ ^(GET|HEAD|POST|OPTIONS)$ ) {
|
|
||||||
return 444;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($host != 'deepzz.com' ) {
|
|
||||||
rewrite ^/(.*)$ https://deepzz.com/$1 permanent;
|
|
||||||
}
|
|
||||||
|
|
||||||
# webmaster 站点验证相关.
|
|
||||||
location ~* (google4c90d18e696bdcf8\.html|BingSiteAuth\.xml)$ {
|
|
||||||
root /data/eiblog/static;
|
|
||||||
expires 1d;
|
|
||||||
}
|
|
||||||
|
|
||||||
location ^~ /admin/ {
|
|
||||||
proxy_http_version 1.1;
|
|
||||||
|
|
||||||
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload";
|
|
||||||
|
|
||||||
# deny 将完全不允许页面被嵌套,可能会导致一些异常。如果遇到这样的问题,建议改成 SAMEORIGIN.
|
|
||||||
# https://imququ.com/post/web-security-and-response-header.html#toc-1
|
|
||||||
add_header X-Frame-Options deny;
|
|
||||||
add_header X-Powered-By eiblog/1.3.0;
|
|
||||||
add_header X-Content-Type-Options nosniff;
|
|
||||||
|
|
||||||
proxy_set_header Connection "";
|
|
||||||
proxy_set_header Host deepzz.com;
|
|
||||||
proxy_set_header X-Real-IP $remote_addr;
|
|
||||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
|
||||||
|
|
||||||
proxy_pass http://127.0.0.1:9000;
|
|
||||||
}
|
|
||||||
|
|
||||||
location / {
|
|
||||||
proxy_http_version 1.1;
|
|
||||||
|
|
||||||
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload";
|
|
||||||
add_header X-Frame-Options deny;
|
|
||||||
add_header X-Content-Type-Options nosniff;
|
|
||||||
# 改deepzz相关的.
|
|
||||||
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";
|
|
||||||
# 中间证书证书指纹, chrome69 将忽略, 用 expect-ct 替代.
|
|
||||||
# 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;';
|
|
||||||
# 期望 ct.
|
|
||||||
# https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Expect-CT
|
|
||||||
add_header Expect-CT "max-age=180";
|
|
||||||
add_header Cache-Control no-cache;
|
|
||||||
add_header X-Via Aliyun.QingDao;
|
|
||||||
add_header X-XSS-Protection "1; mode=block";
|
|
||||||
add_header X-Powered-By eiblog/1.3.0;
|
|
||||||
|
|
||||||
proxy_ignore_headers Set-Cookie;
|
|
||||||
proxy_hide_header Vary;
|
|
||||||
|
|
||||||
proxy_set_header Connection "";
|
|
||||||
proxy_set_header Host deepzz.com;
|
|
||||||
proxy_set_header X-Real-IP $remote_addr;
|
|
||||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
|
||||||
|
|
||||||
proxy_pass http://127.0.0.1:9000;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
server {
|
|
||||||
server_name www.deepzz.com deepzz.com;
|
|
||||||
server_tokens off;
|
|
||||||
|
|
||||||
access_log /dev/null;
|
|
||||||
|
|
||||||
if ($request_method !~ ^(GET|HEAD|POST|OPTIONS)$ ) {
|
|
||||||
return 444;
|
|
||||||
}
|
|
||||||
|
|
||||||
# letsencrypt file verify
|
|
||||||
location ^~ /.well-known/acme-challenge/ {
|
|
||||||
alias /data/eiblog/challenges/;
|
|
||||||
try_files $uri =404;
|
|
||||||
}
|
|
||||||
|
|
||||||
location / {
|
|
||||||
rewrite ^/(.*)$ https://deepzz.com/$1 permanent;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
# example black list
|
|
||||||
#deny 195.154.211.220;
|
|
||||||
@@ -1,71 +0,0 @@
|
|||||||
|
|
||||||
#user nobody;
|
|
||||||
worker_processes 1;
|
|
||||||
|
|
||||||
#error_log logs/error.log;
|
|
||||||
#error_log logs/error.log notice;
|
|
||||||
#error_log logs/error.log info;
|
|
||||||
|
|
||||||
#pid logs/nginx.pid;
|
|
||||||
|
|
||||||
|
|
||||||
events {
|
|
||||||
worker_connections 10240;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
http {
|
|
||||||
include mime.types;
|
|
||||||
default_type application/octet-stream;
|
|
||||||
charset UTF-8;
|
|
||||||
#log_format main '$remote_addr - $remote_user [$time_local] "$request" '
|
|
||||||
# '$status $body_bytes_sent "$http_referer" '
|
|
||||||
# '"$http_user_agent" "$http_x_forwarded_for"';
|
|
||||||
|
|
||||||
#access_log logs/access.log main;
|
|
||||||
access_log off;
|
|
||||||
|
|
||||||
sendfile on;
|
|
||||||
tcp_nopush on;
|
|
||||||
tcp_nodelay on;
|
|
||||||
|
|
||||||
#keepalive_timeout 0;
|
|
||||||
keepalive_timeout 65;
|
|
||||||
|
|
||||||
gzip on;
|
|
||||||
gzip_vary on;
|
|
||||||
|
|
||||||
gzip_comp_level 6;
|
|
||||||
gzip_buffers 16 8k;
|
|
||||||
|
|
||||||
gzip_min_length 1000;
|
|
||||||
gzip_proxied any;
|
|
||||||
gzip_disable "msie6";
|
|
||||||
|
|
||||||
gzip_http_version 1.0;
|
|
||||||
|
|
||||||
gzip_types text/plain text/css application/json application/x-javascript text/xml application/xml application/xml+rss text/javascript application/javascript image/svg+xml;
|
|
||||||
|
|
||||||
server {
|
|
||||||
listen 80;
|
|
||||||
server_name localhost;
|
|
||||||
|
|
||||||
#charset koi8-r;
|
|
||||||
|
|
||||||
#access_log logs/host.access.log main;
|
|
||||||
|
|
||||||
location / {
|
|
||||||
root html;
|
|
||||||
index index.html index.htm;
|
|
||||||
}
|
|
||||||
|
|
||||||
# redirect server error pages to the static page /50x.html
|
|
||||||
#
|
|
||||||
error_page 500 502 503 504 /50x.html;
|
|
||||||
location = /50x.html {
|
|
||||||
root html;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
include /data/eiblog/conf/nginx/domain/*.conf;
|
|
||||||
}
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
-----BEGIN DH PARAMETERS-----
|
|
||||||
MIIBCAKCAQEAzkJoGFJJGMXQBVIq0DFom7qI3vD7Z8JMQnfCLpoi9AfqW6kGq/bR
|
|
||||||
FhK9fuRkO+GdzZasx1mSNRQeX8GdaQM4GUn0yel7fxlxNC59mxo++P8NvmxQ47l4
|
|
||||||
K9QpIRuqxa5UKIG6g3N5pkLwGjcD9a79v4DJn4XA9cVjRYc4BnYmiArgaMFOmGPy
|
|
||||||
KmvU/VhFv8fnxSfn8uCmAGSuHmfbjx5TMfCqaeXzmmhyvpSl88JZfGlwOtXcOU0K
|
|
||||||
O2JhNRKtaicZlevC8gtpFDNYKnf4K9kiUVmq0JLvuzOxN05sQoPYFCvgMFIYf+ND
|
|
||||||
Jwtv7FWF2hQV3y1Xms7ja4776FcP9QlKuwIBAg==
|
|
||||||
-----END DH PARAMETERS-----
|
|
||||||
@@ -1,59 +0,0 @@
|
|||||||
-----BEGIN CERTIFICATE-----
|
|
||||||
MIIFXzCCBEegAwIBAgIQFcILcawsNlJ6B2Z9hxvG2DANBgkqhkiG9w0BAQsFADCB
|
|
||||||
vTELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQL
|
|
||||||
ExZWZXJpU2lnbiBUcnVzdCBOZXR3b3JrMTowOAYDVQQLEzEoYykgMjAwOCBWZXJp
|
|
||||||
U2lnbiwgSW5jLiAtIEZvciBhdXRob3JpemVkIHVzZSBvbmx5MTgwNgYDVQQDEy9W
|
|
||||||
ZXJpU2lnbiBVbml2ZXJzYWwgUm9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAe
|
|
||||||
Fw0xNjA4MTEwMDAwMDBaFw0yNjA4MTAyMzU5NTlaMIGXMQswCQYDVQQGEwJDTjEl
|
|
||||||
MCMGA1UEChMcVHJ1c3RBc2lhIFRlY2hub2xvZ2llcywgSW5jLjEfMB0GA1UECxMW
|
|
||||||
U3ltYW50ZWMgVHJ1c3QgTmV0d29yazEdMBsGA1UECxMURG9tYWluIFZhbGlkYXRl
|
|
||||||
ZCBTU0wxITAfBgNVBAMTGFRydXN0QXNpYSBEViBTU0wgQ0EgLSBHNjCCASIwDQYJ
|
|
||||||
KoZIhvcNAQEBBQADggEPADCCAQoCggEBAM6PcUG/2YcVEtzwDU9yj/F5Ed4M5LBW
|
|
||||||
8ln6HwMRuXuDgKIz6t6OBeF7tqaZPwZ8NRgwwhtEcnyeuDPjS/HYvQqEZZWQpnut
|
|
||||||
njNGniplXkOngD48f0Bhdo5Rwtqv13UiV59gfP/PSy81atddMqgtt5WeITaseddh
|
|
||||||
4sVZV0bj2b37lJ0w/2TRm+gzIp12lzliIkZr9OdgAKRWpITs0TtU0jJq6gXR5fe7
|
|
||||||
aut4GOVov5SZHJorXJSTkhdEIyoYWgUxNAIu83Ykw91/bjeXKFVvq8+tZZN3ct5F
|
|
||||||
I7qbHRgcrLqRdZQPkKjQWC9hXFrQVZUSwRMjRN2O3TXvmtprFr4od+0CAwEAAaOC
|
|
||||||
AX0wggF5MBIGA1UdEwEB/wQIMAYBAf8CAQAwNgYDVR0fBC8wLTAroCmgJ4YlaHR0
|
|
||||||
cDovL3Muc3ltY2IuY29tL3VuaXZlcnNhbC1yb290LmNybDAdBgNVHSUEFjAUBggr
|
|
||||||
BgEFBQcDAQYIKwYBBQUHAwIwDgYDVR0PAQH/BAQDAgEGMC4GCCsGAQUFBwEBBCIw
|
|
||||||
IDAeBggrBgEFBQcwAYYSaHR0cDovL3Muc3ltY2QuY29tMGEGA1UdIARaMFgwVgYG
|
|
||||||
Z4EMAQIBMEwwIwYIKwYBBQUHAgEWF2h0dHBzOi8vZC5zeW1jYi5jb20vY3BzMCUG
|
|
||||||
CCsGAQUFBwICMBkaF2h0dHBzOi8vZC5zeW1jYi5jb20vcnBhMCkGA1UdEQQiMCCk
|
|
||||||
HjAcMRowGAYDVQQDExFTeW1hbnRlY1BLSS0yLTYwMjAdBgNVHQ4EFgQU3qQwrOQ/
|
|
||||||
9ScrMXyvkGcdriKe7GQwHwYDVR0jBBgwFoAUtnf6aUhHn1MS1cLqBzJ2B9GXBxkw
|
|
||||||
DQYJKoZIhvcNAQELBQADggEBAMS+q79V8jm2p5T8pdMCS8XuYjCVpsJuXh4QqegO
|
|
||||||
Ors1D4nVEE0Thm7V6fy60UgOxsjXayauQonRoCdApv6bziW9USmUK2qQ5xFsgBBD
|
|
||||||
LzwJnQd7FDUW9ngDNCo/377J2CLqiVsYFqNnpuVzbbWF/6UR+AxGwFzzAgx9QOLP
|
|
||||||
Qucpx6kV+d0Ht2cm/+mwGlzmdr/RwcT5iLW0oAQZkcfzbi+s9bBaIprMOCiyvO/F
|
|
||||||
Gf1YDO4KQHMfda+GO6HpmPl8nDFgP17Bi3QgRjr1kjpZ9WvnPQha1wZLel+VGm/e
|
|
||||||
xOXNrbei6P5U58U0Hy3GsO5r/IxRE5k55tR5o5E2EB0ilSg=
|
|
||||||
-----END CERTIFICATE-----
|
|
||||||
-----BEGIN CERTIFICATE-----
|
|
||||||
MIIEuTCCA6GgAwIBAgIQQBrEZCGzEyEDDrvkEhrFHTANBgkqhkiG9w0BAQsFADCB
|
|
||||||
vTELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQL
|
|
||||||
ExZWZXJpU2lnbiBUcnVzdCBOZXR3b3JrMTowOAYDVQQLEzEoYykgMjAwOCBWZXJp
|
|
||||||
U2lnbiwgSW5jLiAtIEZvciBhdXRob3JpemVkIHVzZSBvbmx5MTgwNgYDVQQDEy9W
|
|
||||||
ZXJpU2lnbiBVbml2ZXJzYWwgUm9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAe
|
|
||||||
Fw0wODA0MDIwMDAwMDBaFw0zNzEyMDEyMzU5NTlaMIG9MQswCQYDVQQGEwJVUzEX
|
|
||||||
MBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlTaWduIFRydXN0
|
|
||||||
IE5ldHdvcmsxOjA4BgNVBAsTMShjKSAyMDA4IFZlcmlTaWduLCBJbmMuIC0gRm9y
|
|
||||||
IGF1dGhvcml6ZWQgdXNlIG9ubHkxODA2BgNVBAMTL1ZlcmlTaWduIFVuaXZlcnNh
|
|
||||||
bCBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIIBIjANBgkqhkiG9w0BAQEF
|
|
||||||
AAOCAQ8AMIIBCgKCAQEAx2E3XrEBNNti1xWb/1hajCMj1mCOkdeQmIN65lgZOIzF
|
|
||||||
9uVkhbSicfvtvbnazU0AtMgtc6XHaXGVHzk8skQHnOgO+k1KxCHfKWGPMiJhgsWH
|
|
||||||
H26MfF8WIFFE0XBPV+rjHOPMee5Y2A7Cs0WTwCznmhcrewA3ekEzeOEz4vMQGn+H
|
|
||||||
LL729fdC4uW/h2KJXwBL38Xd5HVEMkE6HnFuacsLdUYI0crSK5XQz/u5QGtkjFdN
|
|
||||||
/BMReYTtXlT2NJ8IAfMQJQYXStrxHXpma5hgZqTZ79IugvHw7wnqRMkVauIDbjPT
|
|
||||||
rJ9VAMf2CGqUuV/c4DPxhGD5WycRtPwW8rtWaoAljQIDAQABo4GyMIGvMA8GA1Ud
|
|
||||||
EwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMG0GCCsGAQUFBwEMBGEwX6FdoFsw
|
|
||||||
WTBXMFUWCWltYWdlL2dpZjAhMB8wBwYFKw4DAhoEFI/l0xqGrI2Oa8PPgGrUSBgs
|
|
||||||
exkuMCUWI2h0dHA6Ly9sb2dvLnZlcmlzaWduLmNvbS92c2xvZ28uZ2lmMB0GA1Ud
|
|
||||||
DgQWBBS2d/ppSEefUxLVwuoHMnYH0ZcHGTANBgkqhkiG9w0BAQsFAAOCAQEASvj4
|
|
||||||
sAPmLGd75JR3Y8xuTPl9Dg3cyLk1uXBPY/ok+myDjEedO2Pzmvl2MpWRsXe8rJq+
|
|
||||||
seQxIcaBlVZaDrHC1LGmWazxY8u4TB1ZkErvkBYoH1quEPuBUDgMbMzxPcP1Y+Oz
|
|
||||||
4yHJJDnp/RVmRvQbEdBNc6N9Rvk97ahfYtTxP/jgdFcrGJ2BtMQo2pSXpXDrrB2+
|
|
||||||
BxHw1dvd5Yzw1TKwg+ZX4o+/vqGqvz0dtdQ46tewXDpPaj+PwGZsY6rp2aQW9IHR
|
|
||||||
lRQOfc2VNNnSj3BzgXucfr2YYdhFh5iQxeuGMMY1v/D/w1WIg0vvBZIGcfK4mJO3
|
|
||||||
7M2CYfE45k+XmCpajQ==
|
|
||||||
-----END CERTIFICATE-----
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
╟░Щ`*dы≤\ЛвH
|
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
<?xml version="1.0"?>
|
<?xml version="1.0"?>
|
||||||
<cross-domain-policy>
|
<cross-domain-policy>
|
||||||
<allow-access-from domain="*.{{.Domain}}" />
|
<allow-access-from domain="*.{{.Host}}" />
|
||||||
</cross-domain-policy>
|
</cross-domain-policy>
|
||||||
|
|||||||
@@ -2,20 +2,20 @@
|
|||||||
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
|
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
|
||||||
<channel>
|
<channel>
|
||||||
<title>{{.Title}}</title>
|
<title>{{.Title}}</title>
|
||||||
<link>https://{{.Domain}}</link>
|
<link>https://{{.Host}}</link>
|
||||||
<description>{{.SubTitle}}</description>
|
<description>{{.SubTitle}}</description>
|
||||||
<atom:link href="https://{{.Domain}}/rss.html" rel="self" />
|
<atom:link href="https://{{.Host}}/rss.html" rel="self" />
|
||||||
<atom:link href="{{.FeedrURL}}" rel="hub" />
|
<atom:link href="{{.FeedrURL}}" rel="hub" />
|
||||||
<language>zh-CN</language>
|
<language>zh-CN</language>
|
||||||
<lastBuildDate>{{.BuildDate}}</lastBuildDate>
|
<lastBuildDate>{{.BuildDate}}</lastBuildDate>
|
||||||
{{range .Artcs}}
|
{{range .Articles}}
|
||||||
<item>
|
<item>
|
||||||
<title>{{.Title}}</title>
|
<title>{{.Title}}</title>
|
||||||
<link>https://{{$.Domain}}/post/{{.Slug}}.html</link>
|
<link>https://{{$.Host}}/post/{{.Slug}}.html</link>
|
||||||
<comments>https://{{$.Domain}}/post/{{.Slug}}.html#comments</comments>
|
<comments>https://{{$.Host}}/post/{{.Slug}}.html#comments</comments>
|
||||||
<guid>https://{{$.Domain}}/post/{{.Slug}}.html</guid>
|
<guid>https://{{$.Host}}/post/{{.Slug}}.html</guid>
|
||||||
<description>
|
<description>
|
||||||
<![CDATA[<blockquote>{{.Content}}<p>本文链接:<a href="https://{{$.Domain}}/post/{{.Slug}}.html">https://{{$.Domain}}/post/{{.Slug}}.html</a>,<a href="https://{{$.Domain}}/post/{{.Slug}}.html#comments">参与评论 »</a></p>]]>
|
<![CDATA[<blockquote>{{.Content}}<p>本文链接:<a href="https://{{$.Host}}/post/{{.Slug}}.html">https://{{$.Host}}/post/{{.Slug}}.html</a>,<a href="https://{{$.Host}}/post/{{.Slug}}.html#comments">参与评论 »</a></p>]]>
|
||||||
</description>
|
</description>
|
||||||
</item>
|
</item>
|
||||||
{{end}}
|
{{end}}
|
||||||
|
|||||||
@@ -2,5 +2,5 @@
|
|||||||
<OpenSearchDescription xmlns="http://a9.com/-/spec/opensearch/1.1/">
|
<OpenSearchDescription xmlns="http://a9.com/-/spec/opensearch/1.1/">
|
||||||
<ShortName>{{.BTitle}}</ShortName>
|
<ShortName>{{.BTitle}}</ShortName>
|
||||||
<Description>{{.SubTitle}}</Description>
|
<Description>{{.SubTitle}}</Description>
|
||||||
<Url type="text/html" template="https://{{.Domain}}/search.html?q={searchTerms}" />
|
<Url type="text/html" template="https://{{.Host}}/search.html?q={searchTerms}" />
|
||||||
</OpenSearchDescription>
|
</OpenSearchDescription>
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
User-agent: *
|
User-agent: *
|
||||||
Allow: /
|
Allow: /
|
||||||
Sitemap: https://{{.Domain}}/sitemap.xml
|
Sitemap: https://{{.Host}}/sitemap.xml
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
<?xml version="1.0" encoding="utf-8" ?>
|
<?xml version="1.0" encoding="utf-8" ?>
|
||||||
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
|
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
|
||||||
{{range .Artcs}}
|
{{range .Articles}}
|
||||||
<url>
|
<url>
|
||||||
<loc>https://{{$.Domain}}/post/{{.Slug}}.html</loc>
|
<loc>https://{{$.Host}}/post/{{.Slug}}.html</loc>
|
||||||
<lastmod>{{dateformat .CreateTime "2006-01-02"}}</lastmod>
|
<lastmod>{{dateformat .CreateTime "2006-01-02"}}</lastmod>
|
||||||
<priority>0.6</priority>
|
<priority>0.6</priority>
|
||||||
</url>
|
</url>
|
||||||
|
|||||||
630
db.go
@@ -1,630 +0,0 @@
|
|||||||
// Package main provides ...
|
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"fmt"
|
|
||||||
"regexp"
|
|
||||||
"sort"
|
|
||||||
"strings"
|
|
||||||
"sync"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/eiblog/blackfriday"
|
|
||||||
"github.com/eiblog/eiblog/setting"
|
|
||||||
"github.com/eiblog/utils/logd"
|
|
||||||
"github.com/eiblog/utils/mgo"
|
|
||||||
)
|
|
||||||
|
|
||||||
// 数据库及表名
|
|
||||||
const (
|
|
||||||
DB = "eiblog"
|
|
||||||
COLLECTION_ACCOUNT = "account"
|
|
||||||
COLLECTION_ARTICLE = "article"
|
|
||||||
COUNTER_SERIE = "serie"
|
|
||||||
COUNTER_ARTICLE = "article"
|
|
||||||
SERIES_MD = "series_md"
|
|
||||||
ARCHIVE_MD = "archive_md"
|
|
||||||
ADD = "add"
|
|
||||||
DELETE = "delete"
|
|
||||||
)
|
|
||||||
|
|
||||||
// blackfriday 配置
|
|
||||||
const (
|
|
||||||
commonHtmlFlags = 0 |
|
|
||||||
blackfriday.HTML_TOC |
|
|
||||||
blackfriday.HTML_USE_XHTML |
|
|
||||||
blackfriday.HTML_USE_SMARTYPANTS |
|
|
||||||
blackfriday.HTML_SMARTYPANTS_FRACTIONS |
|
|
||||||
blackfriday.HTML_SMARTYPANTS_DASHES |
|
|
||||||
blackfriday.HTML_SMARTYPANTS_LATEX_DASHES |
|
|
||||||
blackfriday.HTML_NOFOLLOW_LINKS
|
|
||||||
|
|
||||||
commonExtensions = 0 |
|
|
||||||
blackfriday.EXTENSION_NO_INTRA_EMPHASIS |
|
|
||||||
blackfriday.EXTENSION_TABLES |
|
|
||||||
blackfriday.EXTENSION_FENCED_CODE |
|
|
||||||
blackfriday.EXTENSION_AUTOLINK |
|
|
||||||
blackfriday.EXTENSION_STRIKETHROUGH |
|
|
||||||
blackfriday.EXTENSION_SPACE_HEADERS |
|
|
||||||
blackfriday.EXTENSION_HEADER_IDS |
|
|
||||||
blackfriday.EXTENSION_BACKSLASH_LINE_BREAK |
|
|
||||||
blackfriday.EXTENSION_DEFINITION_LISTS
|
|
||||||
)
|
|
||||||
|
|
||||||
// Global Account
|
|
||||||
var (
|
|
||||||
Ei *Account
|
|
||||||
lock sync.Mutex
|
|
||||||
)
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
// 数据库加索引
|
|
||||||
err := mgo.Index(DB, COLLECTION_ACCOUNT, []string{"username"})
|
|
||||||
if err != nil {
|
|
||||||
logd.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
err = mgo.Index(DB, COLLECTION_ARTICLE, []string{"id"})
|
|
||||||
if err != nil {
|
|
||||||
logd.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
err = mgo.Index(DB, COLLECTION_ARTICLE, []string{"slug"})
|
|
||||||
if err != nil {
|
|
||||||
logd.Fatal(err)
|
|
||||||
}
|
|
||||||
// 读取帐号信息
|
|
||||||
loadAccount()
|
|
||||||
// 获取文章数据
|
|
||||||
loadArticles()
|
|
||||||
// 生成markdown文档
|
|
||||||
go generateMarkdown()
|
|
||||||
// 启动定时器
|
|
||||||
go timer()
|
|
||||||
// 获取评论数量
|
|
||||||
go PostsCount()
|
|
||||||
}
|
|
||||||
|
|
||||||
// 读取或初始化帐号信息
|
|
||||||
func loadAccount() {
|
|
||||||
Ei = &Account{}
|
|
||||||
err := mgo.FindOne(DB, COLLECTION_ACCOUNT, mgo.M{"username": setting.Conf.Account.Username}, Ei)
|
|
||||||
// 初始化用户数据
|
|
||||||
if err == mgo.ErrNotFound {
|
|
||||||
logd.Printf("Initializing account: %s\n", setting.Conf.Account.Username)
|
|
||||||
Ei = &Account{
|
|
||||||
Username: setting.Conf.Account.Username,
|
|
||||||
Password: EncryptPasswd(setting.Conf.Account.Username, setting.Conf.Account.Password),
|
|
||||||
Email: setting.Conf.Account.Email,
|
|
||||||
PhoneN: setting.Conf.Account.PhoneNumber,
|
|
||||||
Address: setting.Conf.Account.Address,
|
|
||||||
CreateTime: time.Now(),
|
|
||||||
}
|
|
||||||
Ei.BlogName = setting.Conf.Blogger.BlogName
|
|
||||||
Ei.SubTitle = setting.Conf.Blogger.SubTitle
|
|
||||||
Ei.BeiAn = setting.Conf.Blogger.BeiAn
|
|
||||||
Ei.BTitle = setting.Conf.Blogger.BTitle
|
|
||||||
Ei.Copyright = setting.Conf.Blogger.Copyright
|
|
||||||
err = mgo.Insert(DB, COLLECTION_ACCOUNT, Ei)
|
|
||||||
generateTopic()
|
|
||||||
} else if err != nil {
|
|
||||||
logd.Fatal(err)
|
|
||||||
}
|
|
||||||
Ei.CH = make(chan string, 2)
|
|
||||||
Ei.MapArticles = make(map[string]*Article)
|
|
||||||
Ei.Tags = make(map[string]SortArticles)
|
|
||||||
}
|
|
||||||
|
|
||||||
func loadArticles() {
|
|
||||||
err := mgo.FindAll(DB, COLLECTION_ARTICLE, mgo.M{"isdraft": false, "deletetime": mgo.M{"$eq": time.Time{}}}, &Ei.Articles)
|
|
||||||
if err != nil {
|
|
||||||
logd.Fatal(err)
|
|
||||||
}
|
|
||||||
sort.Sort(Ei.Articles)
|
|
||||||
for i, v := range Ei.Articles {
|
|
||||||
// 渲染文章
|
|
||||||
GenerateExcerptAndRender(v)
|
|
||||||
Ei.MapArticles[v.Slug] = v
|
|
||||||
// 分析文章
|
|
||||||
if v.ID < setting.Conf.General.StartID {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if i > 0 {
|
|
||||||
v.Prev = Ei.Articles[i-1]
|
|
||||||
}
|
|
||||||
if Ei.Articles[i+1].ID >= setting.Conf.General.StartID {
|
|
||||||
v.Next = Ei.Articles[i+1]
|
|
||||||
}
|
|
||||||
upArticle(v, false)
|
|
||||||
}
|
|
||||||
Ei.CH <- SERIES_MD
|
|
||||||
Ei.CH <- ARCHIVE_MD
|
|
||||||
}
|
|
||||||
|
|
||||||
// generate series,archive markdown
|
|
||||||
func generateMarkdown() {
|
|
||||||
for {
|
|
||||||
switch typ := <-Ei.CH; typ {
|
|
||||||
case SERIES_MD:
|
|
||||||
sort.Sort(Ei.Series)
|
|
||||||
var buffer bytes.Buffer
|
|
||||||
buffer.WriteString(Ei.SeriesSay)
|
|
||||||
buffer.WriteString("\n\n")
|
|
||||||
for _, serie := range Ei.Series {
|
|
||||||
buffer.WriteString(fmt.Sprintf("### %s{#toc-%d}", serie.Name, serie.ID))
|
|
||||||
buffer.WriteString("\n")
|
|
||||||
buffer.WriteString(serie.Desc)
|
|
||||||
buffer.WriteString("\n\n")
|
|
||||||
for _, artc := range serie.Articles {
|
|
||||||
//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.WriteByte('\n')
|
|
||||||
}
|
|
||||||
Ei.PageSeries = string(renderPage(buffer.Bytes()))
|
|
||||||
case ARCHIVE_MD:
|
|
||||||
sort.Sort(Ei.Archives)
|
|
||||||
var buffer bytes.Buffer
|
|
||||||
buffer.WriteString(Ei.ArchivesSay + "\n")
|
|
||||||
|
|
||||||
var (
|
|
||||||
currentYear string
|
|
||||||
gt12Month = len(Ei.Archives) > 12
|
|
||||||
)
|
|
||||||
for _, archive := range Ei.Archives {
|
|
||||||
if gt12Month {
|
|
||||||
year := archive.Time.Format("2006 年")
|
|
||||||
if currentYear != year {
|
|
||||||
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")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ei.PageArchives = string(renderPage(buffer.Bytes()))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// init account: generate blogroll and about page
|
|
||||||
func generateTopic() {
|
|
||||||
about := &Article{
|
|
||||||
ID: mgo.NextVal(DB, COUNTER_ARTICLE),
|
|
||||||
Author: setting.Conf.Account.Username,
|
|
||||||
Title: "关于",
|
|
||||||
Slug: "about",
|
|
||||||
CreateTime: time.Time{},
|
|
||||||
UpdateTime: time.Time{},
|
|
||||||
}
|
|
||||||
// 推送到 disqus
|
|
||||||
go func() { ThreadCreate(about) }()
|
|
||||||
|
|
||||||
blogroll := &Article{
|
|
||||||
ID: mgo.NextVal(DB, COUNTER_ARTICLE),
|
|
||||||
Author: setting.Conf.Account.Username,
|
|
||||||
Title: "友情链接",
|
|
||||||
Slug: "blogroll",
|
|
||||||
CreateTime: time.Time{},
|
|
||||||
UpdateTime: time.Time{},
|
|
||||||
}
|
|
||||||
err := mgo.Insert(DB, COLLECTION_ARTICLE, blogroll)
|
|
||||||
if err != nil {
|
|
||||||
logd.Fatal(err)
|
|
||||||
}
|
|
||||||
err = mgo.Insert(DB, COLLECTION_ARTICLE, about)
|
|
||||||
if err != nil {
|
|
||||||
logd.Fatal(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// render page
|
|
||||||
func renderPage(md []byte) []byte {
|
|
||||||
renderer := blackfriday.HtmlRenderer(commonHtmlFlags, "", "")
|
|
||||||
return blackfriday.Markdown(md, renderer, commonExtensions)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 文章分页
|
|
||||||
func PageList(p, n int) (prev int, next int, artcs []*Article) {
|
|
||||||
var l int
|
|
||||||
for l = len(Ei.Articles); l > 0; l-- {
|
|
||||||
if Ei.Articles[l-1].ID >= setting.Conf.General.StartID {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if l == 0 {
|
|
||||||
return 0, 0, nil
|
|
||||||
}
|
|
||||||
m := l / n
|
|
||||||
if d := l % n; d > 0 {
|
|
||||||
m++
|
|
||||||
}
|
|
||||||
if p > m {
|
|
||||||
p = m
|
|
||||||
}
|
|
||||||
if p > 1 {
|
|
||||||
prev = p - 1
|
|
||||||
}
|
|
||||||
if p < m {
|
|
||||||
next = p + 1
|
|
||||||
}
|
|
||||||
s := (p - 1) * n
|
|
||||||
e := p * n
|
|
||||||
if e > l {
|
|
||||||
e = l
|
|
||||||
}
|
|
||||||
artcs = Ei.Articles[s:e]
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// 渲染markdown操作和截取摘要操作
|
|
||||||
var reg = regexp.MustCompile(setting.Conf.General.Identifier)
|
|
||||||
|
|
||||||
// header
|
|
||||||
var regH = regexp.MustCompile("</nav></div>")
|
|
||||||
|
|
||||||
func GenerateExcerptAndRender(artc *Article) {
|
|
||||||
if strings.HasPrefix(artc.Content, setting.Conf.General.DescPrefix) {
|
|
||||||
index := strings.Index(artc.Content, "\r\n")
|
|
||||||
artc.Desc = IgnoreHtmlTag(artc.Content[len(setting.Conf.General.DescPrefix):index])
|
|
||||||
artc.Content = artc.Content[index:]
|
|
||||||
}
|
|
||||||
|
|
||||||
// 查找目录
|
|
||||||
content := renderPage([]byte(artc.Content))
|
|
||||||
index := regH.FindIndex(content)
|
|
||||||
if index != nil {
|
|
||||||
artc.Header = string(content[0:index[1]])
|
|
||||||
artc.Content = string(content[index[1]:])
|
|
||||||
} else {
|
|
||||||
artc.Content = string(content)
|
|
||||||
}
|
|
||||||
index = reg.FindStringIndex(artc.Content)
|
|
||||||
if index != nil {
|
|
||||||
artc.Excerpt = IgnoreHtmlTag(artc.Content[0:index[0]])
|
|
||||||
} else {
|
|
||||||
uc := []rune(artc.Content)
|
|
||||||
length := setting.Conf.General.Length
|
|
||||||
if len(uc) < length {
|
|
||||||
length = len(uc)
|
|
||||||
}
|
|
||||||
artc.Excerpt = IgnoreHtmlTag(string(uc[0:length]))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 读取草稿箱
|
|
||||||
func LoadDraft() (artcs SortArticles, err error) {
|
|
||||||
err = mgo.FindAll(DB, COLLECTION_ARTICLE, mgo.M{"isdraft": true}, &artcs)
|
|
||||||
sort.Sort(artcs)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// 读取回收箱
|
|
||||||
func LoadTrash() (artcs SortArticles, err error) {
|
|
||||||
err = mgo.FindAll(DB, COLLECTION_ARTICLE, mgo.M{"deletetime": mgo.M{"$ne": time.Time{}}}, &artcs)
|
|
||||||
sort.Sort(artcs)
|
|
||||||
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) {
|
|
||||||
Ei.MapArticles[newArtc.Slug] = newArtc
|
|
||||||
GenerateExcerptAndRender(newArtc)
|
|
||||||
if newArtc.ID < setting.Conf.General.StartID {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if oldArtc != nil {
|
|
||||||
i, artc := GetArticle(oldArtc.ID)
|
|
||||||
DelFromLinkedList(artc)
|
|
||||||
Ei.Articles = append(Ei.Articles[:i], Ei.Articles[i+1:]...)
|
|
||||||
|
|
||||||
dropArticle(oldArtc)
|
|
||||||
}
|
|
||||||
|
|
||||||
Ei.Articles = append(Ei.Articles, newArtc)
|
|
||||||
sort.Sort(Ei.Articles)
|
|
||||||
AddToLinkedList(newArtc.ID)
|
|
||||||
|
|
||||||
upArticle(newArtc, true)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 添加文章
|
|
||||||
func AddArticle(artc *Article) error {
|
|
||||||
// 分配ID, 占位至起始id
|
|
||||||
for {
|
|
||||||
if id := mgo.NextVal(DB, COUNTER_ARTICLE); id < setting.Conf.General.StartID {
|
|
||||||
continue
|
|
||||||
} else {
|
|
||||||
artc.ID = id
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
err := mgo.Insert(DB, COLLECTION_ARTICLE, artc)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// 正式发布文章
|
|
||||||
if !artc.IsDraft {
|
|
||||||
defer GenerateExcerptAndRender(artc)
|
|
||||||
Ei.MapArticles[artc.Slug] = artc
|
|
||||||
Ei.Articles = append([]*Article{artc}, Ei.Articles...)
|
|
||||||
sort.Sort(Ei.Articles)
|
|
||||||
AddToLinkedList(artc.ID)
|
|
||||||
|
|
||||||
upArticle(artc, true)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// 删除文章,移入回收箱
|
|
||||||
func DelArticles(ids ...int32) error {
|
|
||||||
lock.Lock()
|
|
||||||
defer lock.Unlock()
|
|
||||||
for _, id := range ids {
|
|
||||||
i, artc := GetArticle(id)
|
|
||||||
DelFromLinkedList(artc)
|
|
||||||
Ei.Articles = append(Ei.Articles[:i], Ei.Articles[i+1:]...)
|
|
||||||
delete(Ei.MapArticles, artc.Slug)
|
|
||||||
|
|
||||||
err := UpdateArticle(mgo.M{"id": id}, mgo.M{"$set": mgo.M{"deletetime": time.Now()}})
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
dropArticle(artc)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// 从链表里删除文章
|
|
||||||
func DelFromLinkedList(artc *Article) {
|
|
||||||
if artc.Prev == nil && artc.Next != nil {
|
|
||||||
artc.Next.Prev = nil
|
|
||||||
} else if artc.Prev != nil && artc.Next == nil {
|
|
||||||
artc.Prev.Next = nil
|
|
||||||
} else if artc.Prev != nil && artc.Next != nil {
|
|
||||||
artc.Prev.Next = artc.Next
|
|
||||||
artc.Next.Prev = artc.Prev
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 将文章添加到链表
|
|
||||||
func AddToLinkedList(id int32) {
|
|
||||||
i, artc := GetArticle(id)
|
|
||||||
if i == 0 && Ei.Articles[i+1].ID >= setting.Conf.General.StartID {
|
|
||||||
artc.Next = Ei.Articles[i+1]
|
|
||||||
Ei.Articles[i+1].Prev = artc
|
|
||||||
} else if i > 0 && Ei.Articles[i-1].ID >= setting.Conf.General.StartID {
|
|
||||||
artc.Prev = Ei.Articles[i-1]
|
|
||||||
if Ei.Articles[i-1].Next != nil {
|
|
||||||
artc.Next = Ei.Articles[i-1].Next
|
|
||||||
Ei.Articles[i-1].Next.Prev = artc
|
|
||||||
}
|
|
||||||
Ei.Articles[i-1].Next = artc
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 从缓存获取文章
|
|
||||||
func GetArticle(id int32) (int, *Article) {
|
|
||||||
for i, artc := range Ei.Articles {
|
|
||||||
if id == artc.ID {
|
|
||||||
return i, artc
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return -1, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// 定时清除回收箱文章
|
|
||||||
func timer() {
|
|
||||||
delT := time.NewTicker(time.Duration(setting.Conf.General.Clean) * time.Hour)
|
|
||||||
for {
|
|
||||||
<-delT.C
|
|
||||||
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 mgo.M) error {
|
|
||||||
return mgo.Update(DB, COLLECTION_ACCOUNT, mgo.M{"username": Ei.Username}, M)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 删除草稿箱或回收箱,永久删除
|
|
||||||
func RemoveArticle(id int32) error {
|
|
||||||
return mgo.Remove(DB, COLLECTION_ARTICLE, mgo.M{"id": id})
|
|
||||||
}
|
|
||||||
|
|
||||||
// 恢复删除文章到草稿箱
|
|
||||||
func RecoverArticle(id int32) error {
|
|
||||||
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 {
|
|
||||||
return mgo.Update(DB, COLLECTION_ARTICLE, query, update)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 编辑文档
|
|
||||||
func QueryArticle(id int32) *Article {
|
|
||||||
artc := &Article{}
|
|
||||||
if err := mgo.FindOne(DB, COLLECTION_ARTICLE, mgo.M{"id": id}, artc); err != nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return artc
|
|
||||||
}
|
|
||||||
|
|
||||||
// 添加专题
|
|
||||||
func AddSerie(name, slug, desc string) error {
|
|
||||||
serie := &Serie{mgo.NextVal(DB, COUNTER_SERIE), name, slug, desc, time.Now(), nil}
|
|
||||||
Ei.Series = append(Ei.Series, serie)
|
|
||||||
sort.Sort(Ei.Series)
|
|
||||||
Ei.CH <- SERIES_MD
|
|
||||||
return UpdateAccountField(mgo.M{"$addToSet": mgo.M{"blogger.series": serie}})
|
|
||||||
}
|
|
||||||
|
|
||||||
// 更新专题
|
|
||||||
func UpdateSerie(serie *Serie) error {
|
|
||||||
Ei.CH <- SERIES_MD
|
|
||||||
return mgo.Update(DB, COLLECTION_ACCOUNT, mgo.M{"username": Ei.Username,
|
|
||||||
"blogger.series.id": serie.ID}, mgo.M{"$set": mgo.M{"blogger.series.$": serie}})
|
|
||||||
}
|
|
||||||
|
|
||||||
// 删除专题
|
|
||||||
func DelSerie(id int32) error {
|
|
||||||
for i, serie := range Ei.Series {
|
|
||||||
if id == serie.ID {
|
|
||||||
if len(serie.Articles) > 0 {
|
|
||||||
return fmt.Errorf("请删除该专题下的所有文章")
|
|
||||||
}
|
|
||||||
err := UpdateAccountField(mgo.M{"$pull": mgo.M{"blogger.series": mgo.M{"id": id}}})
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
Ei.Series[i] = nil
|
|
||||||
Ei.Series = append(Ei.Series[:i], Ei.Series[i+1:]...)
|
|
||||||
Ei.CH <- SERIES_MD
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// 查找专题
|
|
||||||
func QuerySerie(id int32) *Serie {
|
|
||||||
for _, serie := range Ei.Series {
|
|
||||||
if serie.ID == id {
|
|
||||||
return serie
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// 后台分页
|
|
||||||
func PageListBack(se int, kw string, draft, del bool, p, n int) (max int, artcs []*Article) {
|
|
||||||
M := mgo.M{}
|
|
||||||
if draft {
|
|
||||||
M["isdraft"] = true
|
|
||||||
} else if del {
|
|
||||||
M["deletetime"] = mgo.M{"$ne": time.Time{}}
|
|
||||||
} else {
|
|
||||||
M["isdraft"] = false
|
|
||||||
M["deletetime"] = mgo.M{"$eq": time.Time{}}
|
|
||||||
if se > 0 {
|
|
||||||
M["serieid"] = se
|
|
||||||
}
|
|
||||||
if kw != "" {
|
|
||||||
M["title"] = mgo.M{"$regex": kw, "$options": "$i"}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ms, c := mgo.Connect(DB, COLLECTION_ARTICLE)
|
|
||||||
defer ms.Close()
|
|
||||||
err := c.Find(M).Select(mgo.M{"content": 0}).Sort("-createtime").Limit(n).Skip((p - 1) * n).All(&artcs)
|
|
||||||
if err != nil {
|
|
||||||
logd.Error(err)
|
|
||||||
}
|
|
||||||
count, err := c.Find(M).Count()
|
|
||||||
if err != nil {
|
|
||||||
logd.Error(err)
|
|
||||||
}
|
|
||||||
max = count / n
|
|
||||||
if count%n > 0 {
|
|
||||||
max++
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
62
db_test.go
@@ -1,62 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"html/template"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestPageListBack(t *testing.T) {
|
|
||||||
_, artcs := PageListBack(0, "", false, false, 1, 20)
|
|
||||||
for _, artc := range artcs {
|
|
||||||
t.Log(*artc)
|
|
||||||
}
|
|
||||||
t.Log("------------------------------------------------------------")
|
|
||||||
_, artcs = PageListBack(0, "", false, false, 2, 10)
|
|
||||||
for _, artc := range artcs {
|
|
||||||
t.Log(*artc)
|
|
||||||
}
|
|
||||||
t.Log("------------------------------------------------------------")
|
|
||||||
_, artcs = PageListBack(3, "", false, false, 1, 20)
|
|
||||||
for _, artc := range artcs {
|
|
||||||
t.Log(*artc)
|
|
||||||
}
|
|
||||||
t.Log("------------------------------------------------------------")
|
|
||||||
_, artcs = PageListBack(3, "19", false, false, 1, 20)
|
|
||||||
for _, artc := range artcs {
|
|
||||||
t.Log(*artc)
|
|
||||||
}
|
|
||||||
t.Log("------------------------------------------------------------")
|
|
||||||
_, artcs = PageListBack(0, "", false, true, 1, 20)
|
|
||||||
for _, artc := range artcs {
|
|
||||||
t.Log(*artc)
|
|
||||||
}
|
|
||||||
t.Log("------------------------------------------------------------")
|
|
||||||
_, artcs = PageListBack(0, "", true, false, 1, 20)
|
|
||||||
for _, artc := range artcs {
|
|
||||||
t.Log(*artc)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestAddSerie(t *testing.T) {
|
|
||||||
err := AddSerie("测试", "nothing", "这里是描述")
|
|
||||||
if err != nil {
|
|
||||||
t.Error(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestRenderPage(t *testing.T) {
|
|
||||||
data := []byte(`<ul class="links ssl">
|
|
||||||
<li><a href="https://yryz.net/">一人游走</a><span class="date">「不错的小伙子」</span></li>
|
|
||||||
<li><a href="https://hsulei.com/">Leo同学</a><span class="date">「小伙子,该干活了」</span></li>
|
|
||||||
<li><a href="https://razeencheng.com/">razeen同学</a><span class="date">「Stay hungry. Stay foolish.」</span></li>
|
|
||||||
</ul>
|
|
||||||
|
|
||||||
<ul class="links">
|
|
||||||
<li><a href="http://blog.mirreal.net/">Mirreal Ellison</a><span class="date">「kissing the fire」</span></li>
|
|
||||||
</ul>`)
|
|
||||||
|
|
||||||
t.Log(IgnoreHtmlTag(string(data)))
|
|
||||||
data = renderPage(data)
|
|
||||||
|
|
||||||
t.Log(template.HTML(string(data)))
|
|
||||||
}
|
|
||||||
380
disqus.go
@@ -1,380 +0,0 @@
|
|||||||
// Package main provides ...
|
|
||||||
// Get article' comments count
|
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"crypto/tls"
|
|
||||||
"encoding/json"
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"io/ioutil"
|
|
||||||
"net"
|
|
||||||
"net/http"
|
|
||||||
"net/url"
|
|
||||||
"strings"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/deepzz0/logd"
|
|
||||||
"github.com/eiblog/eiblog/setting"
|
|
||||||
)
|
|
||||||
|
|
||||||
var ErrDisqusConfig = errors.New("disqus config incorrect")
|
|
||||||
|
|
||||||
func correctDisqusConfig() bool {
|
|
||||||
return setting.Conf.Disqus.PostsCount != "" &&
|
|
||||||
setting.Conf.Disqus.PublicKey != "" &&
|
|
||||||
setting.Conf.Disqus.ShortName != ""
|
|
||||||
}
|
|
||||||
|
|
||||||
// 定时获取所有文章评论数量
|
|
||||||
type postsCountResp struct {
|
|
||||||
Code int
|
|
||||||
Response []struct {
|
|
||||||
Id string
|
|
||||||
Posts int
|
|
||||||
Identifiers []string
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func PostsCount() error {
|
|
||||||
if !correctDisqusConfig() {
|
|
||||||
return ErrDisqusConfig
|
|
||||||
}
|
|
||||||
|
|
||||||
time.AfterFunc(time.Duration(setting.Conf.Disqus.Interval)*time.Hour, func() {
|
|
||||||
err := PostsCount()
|
|
||||||
if err != nil {
|
|
||||||
logd.Error(err)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
vals := url.Values{}
|
|
||||||
vals.Set("api_key", setting.Conf.Disqus.PublicKey)
|
|
||||||
vals.Set("forum", setting.Conf.Disqus.ShortName)
|
|
||||||
|
|
||||||
var count, index int
|
|
||||||
for index < len(Ei.Articles) {
|
|
||||||
for ; index < len(Ei.Articles) && count < 50; index++ {
|
|
||||||
artc := Ei.Articles[index]
|
|
||||||
vals.Add("thread:ident", "post-"+artc.Slug)
|
|
||||||
count++
|
|
||||||
}
|
|
||||||
count = 0
|
|
||||||
resp, err := Get(setting.Conf.Disqus.PostsCount + "?" + vals.Encode())
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer resp.Body.Close()
|
|
||||||
|
|
||||||
b, err := ioutil.ReadAll(resp.Body)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if resp.StatusCode != http.StatusOK {
|
|
||||||
return errors.New(string(b))
|
|
||||||
}
|
|
||||||
|
|
||||||
result := &postsCountResp{}
|
|
||||||
err = json.Unmarshal(b, result)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
for _, v := range result.Response {
|
|
||||||
i := strings.Index(v.Identifiers[0], "-")
|
|
||||||
artc := Ei.MapArticles[v.Identifiers[0][i+1:]]
|
|
||||||
if artc != nil {
|
|
||||||
artc.Count = v.Posts
|
|
||||||
artc.Thread = v.Id
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// 获取文章评论列表
|
|
||||||
type postsListResp struct {
|
|
||||||
Cursor struct {
|
|
||||||
HasNext bool
|
|
||||||
Next string
|
|
||||||
}
|
|
||||||
Code int
|
|
||||||
Response []postDetail
|
|
||||||
}
|
|
||||||
|
|
||||||
type postDetail struct {
|
|
||||||
Parent int
|
|
||||||
Id string
|
|
||||||
CreatedAt string
|
|
||||||
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
|
|
||||||
}
|
|
||||||
|
|
||||||
vals := url.Values{}
|
|
||||||
vals.Set("api_key", setting.Conf.Disqus.PublicKey)
|
|
||||||
vals.Set("forum", setting.Conf.Disqus.ShortName)
|
|
||||||
vals.Set("thread:ident", "post-"+slug)
|
|
||||||
vals.Set("cursor", cursor)
|
|
||||||
vals.Set("limit", "50")
|
|
||||||
|
|
||||||
resp, err := Get(setting.Conf.Disqus.PostsList + "?" + vals.Encode())
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
defer resp.Body.Close()
|
|
||||||
|
|
||||||
b, err := ioutil.ReadAll(resp.Body)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if resp.StatusCode != http.StatusOK {
|
|
||||||
return nil, errors.New(string(b))
|
|
||||||
}
|
|
||||||
|
|
||||||
result := &postsListResp{}
|
|
||||||
err = json.Unmarshal(b, result)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return result, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
type PostComment struct {
|
|
||||||
Message string
|
|
||||||
Parent string
|
|
||||||
Thread string
|
|
||||||
AuthorEmail string
|
|
||||||
AuthorName string
|
|
||||||
IpAddress string
|
|
||||||
Identifier string
|
|
||||||
UserAgent string
|
|
||||||
}
|
|
||||||
|
|
||||||
type postCreateResp struct {
|
|
||||||
Code int
|
|
||||||
Response postDetail
|
|
||||||
}
|
|
||||||
|
|
||||||
// 评论文章
|
|
||||||
func PostCreate(pc *PostComment) (*postCreateResp, error) {
|
|
||||||
if !correctDisqusConfig() {
|
|
||||||
return nil, ErrDisqusConfig
|
|
||||||
}
|
|
||||||
|
|
||||||
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")
|
|
||||||
|
|
||||||
header := http.Header{"Referer": {"https://disqus.com"}}
|
|
||||||
resp, err := PostWithHeader(setting.Conf.Disqus.PostCreate, vals, header)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
defer resp.Body.Close()
|
|
||||||
b, err := ioutil.ReadAll(resp.Body)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if resp.StatusCode != http.StatusOK {
|
|
||||||
return nil, errors.New(string(b))
|
|
||||||
}
|
|
||||||
result := &postCreateResp{}
|
|
||||||
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 {
|
|
||||||
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("post", post)
|
|
||||||
|
|
||||||
header := http.Header{"Referer": {"https://disqus.com"}}
|
|
||||||
resp, err := PostWithHeader(setting.Conf.Disqus.PostApprove, vals, header)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer resp.Body.Close()
|
|
||||||
|
|
||||||
b, err := ioutil.ReadAll(resp.Body)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if resp.StatusCode != http.StatusOK {
|
|
||||||
return errors.New(string(b))
|
|
||||||
}
|
|
||||||
|
|
||||||
result := &approvedResp{}
|
|
||||||
err = json.Unmarshal(b, result)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
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 := 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
|
|
||||||
}
|
|
||||||
|
|
||||||
///////////////////////////// HTTP 请求 /////////////////////////////
|
|
||||||
|
|
||||||
var httpClient = &http.Client{
|
|
||||||
Transport: &http.Transport{
|
|
||||||
Proxy: http.ProxyFromEnvironment,
|
|
||||||
DialContext: (&net.Dialer{
|
|
||||||
Timeout: 30 * time.Second,
|
|
||||||
KeepAlive: 30 * time.Second,
|
|
||||||
DualStack: true,
|
|
||||||
}).DialContext,
|
|
||||||
ForceAttemptHTTP2: true,
|
|
||||||
MaxIdleConns: 100,
|
|
||||||
IdleConnTimeout: 90 * time.Second,
|
|
||||||
TLSHandshakeTimeout: 10 * time.Second,
|
|
||||||
ExpectContinueTimeout: 1 * time.Second,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
func newRequest(method, rawurl string, vals url.Values) (*http.Request, error) {
|
|
||||||
u, err := url.Parse(rawurl)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
host := u.Host
|
|
||||||
// 获取主机IP
|
|
||||||
ips, err := net.LookupHost(u.Host)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if len(ips) == 0 {
|
|
||||||
return nil, errors.New("not found ip: " + u.Host)
|
|
||||||
}
|
|
||||||
// 设置ServerName
|
|
||||||
httpClient.Transport.(*http.Transport).TLSClientConfig = &tls.Config{
|
|
||||||
InsecureSkipVerify: true,
|
|
||||||
}
|
|
||||||
u.Host = ips[0]
|
|
||||||
// 创建HTTP Request
|
|
||||||
var req *http.Request
|
|
||||||
if vals != nil {
|
|
||||||
req, err = http.NewRequest(method, u.String(), strings.NewReader(vals.Encode()))
|
|
||||||
} else {
|
|
||||||
req, err = http.NewRequest(method, u.String(), nil)
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
// 改变Host
|
|
||||||
req.Host = host
|
|
||||||
return req, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get HTTP Get请求
|
|
||||||
func Get(rawurl string) (*http.Response, error) {
|
|
||||||
req, err := newRequest(http.MethodGet, rawurl, nil)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
// 发起请求
|
|
||||||
return httpClient.Do(req)
|
|
||||||
}
|
|
||||||
|
|
||||||
// PostForm HTTP Post请求
|
|
||||||
func PostForm(rawurl string, vals url.Values) (*http.Response, error) {
|
|
||||||
req, err := newRequest(http.MethodPost, rawurl, vals)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
|
|
||||||
// 发起请求
|
|
||||||
return httpClient.Do(req)
|
|
||||||
}
|
|
||||||
|
|
||||||
// PostWithHeader HTTP Post请求,自定义Header
|
|
||||||
func PostWithHeader(rawurl string, vals url.Values, header http.Header) (*http.Response, error) {
|
|
||||||
req, err := newRequest(http.MethodPost, rawurl, vals)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
// set header
|
|
||||||
req.Header = header
|
|
||||||
// 发起请求
|
|
||||||
return httpClient.Do(req)
|
|
||||||
}
|
|
||||||
@@ -1,36 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestDisqus(t *testing.T) {
|
|
||||||
PostsCount()
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestPostCreate(t *testing.T) {
|
|
||||||
pc := &PostComment{
|
|
||||||
Message: "hahahaha",
|
|
||||||
Thread: "52799014",
|
|
||||||
AuthorEmail: "deepzz.qi@gmail.com",
|
|
||||||
AuthorName: "deepzz",
|
|
||||||
}
|
|
||||||
|
|
||||||
id, err := PostCreate(pc)
|
|
||||||
if err != nil {
|
|
||||||
t.Error(err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
18
dist.sh
@@ -1,18 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
|
|
||||||
# GOFLAGS='-ldflags="-s -w"'
|
|
||||||
version=`git describe --tags`
|
|
||||||
arch=$(go env GOARCH)
|
|
||||||
|
|
||||||
for os in linux darwin windows; do
|
|
||||||
echo "... building $version for $os/$arch"
|
|
||||||
TARGET="eiblog-$version.$os-$arch"
|
|
||||||
GOOS=$os GOARCH=$arch CGO_ENABLED=0 go build
|
|
||||||
if [ "$os" == "windows" ]; then
|
|
||||||
tar czvf $TARGET.tar.gz conf static views eiblog.exe
|
|
||||||
rm eiblog.exe
|
|
||||||
else
|
|
||||||
tar czvf $TARGET.tar.gz conf static views eiblog
|
|
||||||
rm eiblog
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
@@ -1,46 +0,0 @@
|
|||||||
version: '2'
|
|
||||||
services:
|
|
||||||
mongodb:
|
|
||||||
image: mongo:3.2
|
|
||||||
container_name: eidb
|
|
||||||
volumes:
|
|
||||||
- /data/eiblog/mgodb:/data/db
|
|
||||||
restart: always
|
|
||||||
elasticsearch:
|
|
||||||
image: elasticsearch:2.4.1
|
|
||||||
container_name: eisearch
|
|
||||||
volumes:
|
|
||||||
- /data/eiblog/conf/es/config:/usr/share/elasticsearch/config
|
|
||||||
- /data/eiblog/conf/es/plugins:/usr/share/elasticsearch/plugins
|
|
||||||
- /data/eiblog/esdata/data:/usr/share/elasticsearch/data
|
|
||||||
- /data/eiblog/esdata/logs:/usr/share/elasticsearch/logs
|
|
||||||
environment:
|
|
||||||
ES_JAVA_OPTS: "-Xms512m -Xmx512m"
|
|
||||||
restart: always
|
|
||||||
eiblog:
|
|
||||||
image: registry.cn-hangzhou.aliyuncs.com/deepzz/eiblog
|
|
||||||
container_name: eiblog
|
|
||||||
extra_hosts:
|
|
||||||
- "disqus.com:23.235.33.134"
|
|
||||||
volumes:
|
|
||||||
- /data/eiblog/logdata:/eiblog/logdata
|
|
||||||
- /data/eiblog/conf:/eiblog/conf
|
|
||||||
links:
|
|
||||||
- elasticsearch
|
|
||||||
- mongodb
|
|
||||||
environment:
|
|
||||||
- GODEBUG=netdns=cgo
|
|
||||||
ports:
|
|
||||||
- "9000:9000"
|
|
||||||
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
|
|
||||||
1
docs/README.md
Normal file
@@ -0,0 +1 @@
|
|||||||
|
Design and user documents (in addition to your godoc generated documentation).
|
||||||
@@ -1 +0,0 @@
|
|||||||
theme: jekyll-theme-cayman
|
|
||||||
@@ -1,24 +0,0 @@
|
|||||||
### Twitter Card
|
|
||||||
相信很多人不明白为什么会这样专注twitter。首先twitter是一个社交网站,国际性的。其次我们可以使用它的Twitter Card功能非常的酷。
|
|
||||||
|
|
||||||
当你配置好Twitter相关的参数后`conf/app.yml`:
|
|
||||||
```
|
|
||||||
# twitter地址: twitter.com/chenqijing2
|
|
||||||
twitter:
|
|
||||||
card: summary
|
|
||||||
site: chenqijing2
|
|
||||||
image: st.deepzz.com/static/img/avatar.jpg
|
|
||||||
address: twitter.com/chenqijing2
|
|
||||||
```
|
|
||||||
|
|
||||||
每当你发部一个推文,你如果带上你的网址,它会自动给你展示成卡片的形式
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||

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

|
|
||||||
@@ -1,69 +0,0 @@
|
|||||||
### 证书自动更新
|
|
||||||
|
|
||||||
本博客证书自动更新有两种方式:
|
|
||||||
|
|
||||||
* [acme/autocert](https://github.com/golang/crypto/tree/master/acme/autocert),博客内部集成,通过 tls-sni 验证,实现全自动更新证书,一键开启关闭。请在裸服务器的情况下使用(不要使用代理)。单证书
|
|
||||||
* [acme.sh](https://github.com/Neilpang/acme.sh),强大的 acme 脚本,多种自动更新证书方式,满足你各方面的需求。双证书
|
|
||||||
|
|
||||||
#### 方式一
|
|
||||||
什么是 autocert,简单点,你只需要两步操作:
|
|
||||||
|
|
||||||
1. 将域名解析到你的服务器。
|
|
||||||
2. 在服务器上运行开启 autocert 功能的程序(这里不需要配置证书),需要占用 443 端口。
|
|
||||||
|
|
||||||
其它过程你不需要过问,即会完成自动申请证书,自动更新证书的功能(默认 30 天)。这个是在 tcp/ip 层的操作,对用户完全透明,非常棒。
|
|
||||||
|
|
||||||
一键开启 autocert 功能,只需修改 `conf/app.yml` 文件内容:
|
|
||||||
|
|
||||||
```
|
|
||||||
# 运行模式
|
|
||||||
mode:
|
|
||||||
# http server
|
|
||||||
enablehttp: true
|
|
||||||
httpport: 9000
|
|
||||||
# https server
|
|
||||||
enablehttps: true # 必须开启
|
|
||||||
autocert: false # autocert 功能开关
|
|
||||||
httpsport: 9001
|
|
||||||
certfile:
|
|
||||||
keyfile:
|
|
||||||
domain: deepzz.com # 申请证书的域名,也是博客的域名
|
|
||||||
```
|
|
||||||
|
|
||||||
首先,使用 HTTPS 必须启用 `enablehttps`,它有两个作用:
|
|
||||||
|
|
||||||
* 如果 `enablehttp` 开启,会自动 301 重定向到 https。
|
|
||||||
* 作为开启 autocert 的前提条件。
|
|
||||||
|
|
||||||
其次, `autocert` 是否开启也有两个作用:
|
|
||||||
|
|
||||||
* false,服务器将使用 `httpsport`、`certfile`、`keyfile` 作为参数启动 https 服务器。
|
|
||||||
* true,服务器直接使用 443 端口启动 https 服务器,并且自动申请证书,且在证书只有 30 天有效期时自动更新证书。域名为 *运行模式* 下的 mode->domain。
|
|
||||||
|
|
||||||
#### 方式二
|
|
||||||
|
|
||||||
使用方式二,你需要了解 acme.sh 的具体使用方式,非常简单。选择适合自己的方式实现自动更新证书。
|
|
||||||
|
|
||||||
博主,这里实现了 aliyun dns 的自动验证,自动更新证书。详情参见 Makefile->gencert。这里实现了自动申请 ecc、rsa 双证书,并且自动申请 scts,自动安装,自动更新。
|
|
||||||
|
|
||||||
基本流程如下:
|
|
||||||
|
|
||||||
1. 创建相关目录:`/data/eiblog/conf/ssl`、`/data/eiblog/conf/scts/rsa`、`/data/eiblog/conf/scts/ecc`。
|
|
||||||
2. 自动下载安装 acme.sh 脚本。
|
|
||||||
3. 自动申请 RSA 证书并且自动获取 scts,并且自动安装到指定位置。
|
|
||||||
4. 自动申请 ECC 证书并且自动获取 scts,并且自动安装到指定位置。
|
|
||||||
|
|
||||||
##### 使用方式
|
|
||||||
|
|
||||||
导出环境变量,Aliyun dns 的环境变量为:
|
|
||||||
|
|
||||||
```
|
|
||||||
export Ali_Key="sdfsdfsdfljlbjkljlkjsdfoiwje"
|
|
||||||
export Ali_Secret="jlsdflanljkljlfdsaklkjflsa"
|
|
||||||
```
|
|
||||||
|
|
||||||
执行命令:
|
|
||||||
|
|
||||||
```
|
|
||||||
$ make gencert -cn=common_name -sans="-d example.com -d example1.com"
|
|
||||||
```
|
|
||||||
206
docs/install.md
@@ -1,206 +0,0 @@
|
|||||||
### 安装
|
|
||||||
1、`Eiblog` 提供多个平台的压缩包下载,可到 [Eiblog release](https://github.com/eiblog/eiblog/releases) 选择相应版本和平台下载。也可通过:
|
|
||||||
``` sh
|
|
||||||
$ curl -L https://github.com/eiblog/eiblog/releases/download/v1.0.0/eiblog-v1.0.0.`uname -s | tr '[A-Z]' '[a-z]'`-amd64.tar.gz > eiblog-v1.0.0.`uname -s | tr '[A-Z]' '[a-z]'`-amd64.tar.gz
|
|
||||||
```
|
|
||||||
|
|
||||||
2、如果有幸你也是 `Gopher`,相信你会亲自动手,你可以通过:
|
|
||||||
``` sh
|
|
||||||
$ git clone https://github.com/eiblog/eiblog.git
|
|
||||||
```
|
|
||||||
进行源码编译二进制文件运行。
|
|
||||||
|
|
||||||
3、如果你对 `docker` 技术也有研究的话,你也可以通过 `docker` 来安装:
|
|
||||||
``` sh
|
|
||||||
$ docker pull registry.cn-hangzhou.aliyuncs.com/deepzz/eiblog:v1.2.0
|
|
||||||
```
|
|
||||||
`注意`,镜像内部没有提供 conf 文件夹内的配置内容,因为该内容定制化的需求过高。所以需要将 `conf` 目录映射出来,后面会具体说到。
|
|
||||||
|
|
||||||
### 本地测试
|
|
||||||
采用二进制包进行测试,在下载好可执行程序之后,我们可以开始本地测试的工作了。本地测试需要搭建两个服务 `mongodb` (必须)和 `elasticsearch2.4.1`(可选,搜索服务不可用)。
|
|
||||||
|
|
||||||
`Eiblog ` 默认会连接 `hostname` 为 `mongodb` 和 `elasticsearch` 的地址,因此你需要将信息填入 `/etc/hosts` 下。假如你搭建的 `mongodb` 地址为 `127.0.0.1:27017`,`elasticsearch` 地址为 `192.168.99.100:9200`,如:
|
|
||||||
``` sh
|
|
||||||
$ sudo vi /etc/hosts
|
|
||||||
|
|
||||||
# 在末尾加上两行
|
|
||||||
172.42.0.1 mongodb
|
|
||||||
192.168.99.100 elasticsearch
|
|
||||||
```
|
|
||||||
|
|
||||||
下面先看两个服务的搭建。
|
|
||||||
|
|
||||||
#### MongoDB 搭建
|
|
||||||
|
|
||||||
`MongoDB` 搭建,Mac 可通过 `brew install mongo` 进行安装,其它平台请查询资料。
|
|
||||||
#### Elasticsearch 搭建
|
|
||||||
`Elasticsearch `搭建,它的搭建要些许复杂。建议通过 `docker` 搭建。需要注意的是 es 自带的分析器对中文分词是不友好的,这里采用了 `elasticsearch-analysis-ik` 分词器。如果你想了解更多 [Github](https://github.com/medcl/elasticsearch-analysis-ik) 或则如何实现 [博客站内搜索](https://imququ.com/post/elasticsearch.html)。
|
|
||||||
|
|
||||||
1. pull 镜像 `docker pull elasticsearch:2.4.1`。
|
|
||||||
|
|
||||||
2. 添加环境变量 `ES_JAVA_OPTS: "-Xms512m -Xmx512m"`,除非你想让你的服务器爆掉。
|
|
||||||
|
|
||||||
3. 映射相关目录:
|
|
||||||
|
|
||||||
```
|
|
||||||
$PWD/conf/es/config:/usr/share/elasticsearch/config
|
|
||||||
$PWD/conf/es/plugins:/usr/share/elasticsearch/plugins
|
|
||||||
```
|
|
||||||
|
|
||||||
博主已经准备好了必要的 es 配置文件,请将这四个目录映射至 `eiblog` 下的 `conf` 目录。如果你想查看更多,请查看 `docker-compose.yml` 文件。
|
|
||||||
|
|
||||||
总结一下,`docker` 运行 es 的命令为:
|
|
||||||
``` sh
|
|
||||||
$ docker run -d --name eisearch \
|
|
||||||
-p 9200:9200 \
|
|
||||||
-e ES_JAVA_OPTS="-Xms512m -Xmx512m" \
|
|
||||||
-v $PWD/conf/es/config:/usr/share/elasticsearch/config \
|
|
||||||
-v $PWD/conf/es/plugins:/usr/share/elasticsearch/plugins \
|
|
||||||
elasticsearch:2.4.1
|
|
||||||
```
|
|
||||||
|
|
||||||
之后执行 `./eiblog`,咱们的 `eiblog` 就可以运行起来了。
|
|
||||||
|
|
||||||
通过 `127.0.0.1:9000` 可以进入博客首页,`127.0.0.1:9000/admin/login` 进入后台登陆,账号密码为 `eiblog/conf/app.yml` 下的 `username` 和 `password`。初始账号密码 `deepz`、`deepzz`。
|
|
||||||
|
|
||||||
> `注意`,因为配置 `conf/app.yml` 均是博主自用配置。有些操作可能(如评论)会评论到我的博客,还请尽量避免,谢谢。
|
|
||||||
|
|
||||||
### 准备部署
|
|
||||||
如果你在感受了该博客的魅力了之后,仍然坚持想要搭建它。那么,恭喜你,获得的一款不想再更换的博客系统。下面,我们跟随步骤对部署流程进一步说明。
|
|
||||||
|
|
||||||
这里只提供 `Docker` 的相关部署说明。你如果需要其它方式部署,请参考该方式。
|
|
||||||
|
|
||||||
#### 前提准备
|
|
||||||
这里需要准备一些必要的东西,如果你已准备好。请跳过。
|
|
||||||
|
|
||||||
* `一台服务器`。
|
|
||||||
* `一个域名`,国内服务器需备案。
|
|
||||||
* `有效的证书`。通过开启 autocert 可自动申请更新证书。也可去七牛、qcloud 申请一年有效证书。
|
|
||||||
* `七牛CDN`。博客只设计接入了 七牛cdn,相信该 CDN 服务商不会让你失望。
|
|
||||||
* `Disqus`。作为博客评论系统,你得有翻墙的能力注册到该账号,具体配置我想又可以写一片博客了。简单说需要 `shorname` 和 `public key`。
|
|
||||||
* `Google Analystic`。数据统计分析工具。
|
|
||||||
* `Superfeedr`。加速 RSS 订阅。
|
|
||||||
* `Twitter`。希望你能够有一个 twitter 账号。
|
|
||||||
|
|
||||||
是不是这么多要求,很费解。其实当初该博客系统只是为个人而设计的,是自己心中想要的那一款。博主些这篇文章不是想要多少人来用该博客,而是希望对那些追求至极的朋友说:你需要这款博客系统。
|
|
||||||
#### 文件准备
|
|
||||||
博主是一个有强迫症的人,一些文件的路径我使用了固定的路径,请大家见谅。假如你的 cdn 域名为 `st.example.com`,你需要确定这些文件已经在你的 cdn 中,它们路径分别是:
|
|
||||||
|
|
||||||
| 文件 | 地址 | 描述 |
|
|
||||||
| ------------------ | -------------------------------------------- | ------------------------------------------------------------ |
|
|
||||||
| 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 修改。 |
|
|
||||||
| 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) |
|
|
||||||
| default_avatar.png | st.example.com/static/img/default_avatar.png | disqus 默认图片,[下载](https://st.deepzz.com/static/img/default_avatar.png) |
|
|
||||||
|
|
||||||
> 注意,cdn 提到的文件下载,请复制链接进行下载,因为博主使用了防盗链功能,还有:
|
|
||||||
1、每次修改 app.yml 文件(如:更换 cdn 域名或更新头像),如果你不知道是否应该提高 staticversion 一个版本,那么最好提高一个 +1。
|
|
||||||
2、每次手动修改 views 内的以 `st_` 开头的文件,请将 `app.yml` 中的 staticversion 提高一个版本。
|
|
||||||
|
|
||||||
#### 配置说明
|
|
||||||
走到这里,我相信只走到 `60%` 的路程。放弃还来得及。
|
|
||||||
|
|
||||||
这里会对 `eiblog/conf` 下的所有文件做说明,希望你做好准备。
|
|
||||||
```
|
|
||||||
├── app.yml # 博客配置文件
|
|
||||||
├── blackip.yml # 博客 ip 黑名单
|
|
||||||
├── es # elasticsearch 配置
|
|
||||||
│ ├── config # 配置文件
|
|
||||||
│ │ ├── analysis # 同义词
|
|
||||||
│ │ ├── elasticsearch.yml # 具体配置
|
|
||||||
│ │ ├── logging.yml # 日志配置
|
|
||||||
│ │ └── scripts # 脚本文件夹
|
|
||||||
│ └── plugins # 插件文件夹
|
|
||||||
│ └── ik1.10.1 # ik 分词器
|
|
||||||
├── nginx # nginx 配置
|
|
||||||
│ ├── domain # 域名配置,nginx 会读区改文件夹下的 .conf 文件
|
|
||||||
│ │ └── eiblog.conf
|
|
||||||
│ ├── ip.blacklist # nginx ip黑名单
|
|
||||||
│ └── nginx.conf # nginx 配置,请替换 nginx 原有配置
|
|
||||||
├── scts # ct 透明
|
|
||||||
│ ├── ecc
|
|
||||||
│ │ ├── aviator.sct
|
|
||||||
│ │ └── digicert.sct
|
|
||||||
│ └── rsa
|
|
||||||
│ ├── aviator.sct
|
|
||||||
│ └── digicert.sct
|
|
||||||
├── ssl # 证书相关文件,可参考 eiblog.conf 生成
|
|
||||||
│ ├── dhparams.pem
|
|
||||||
│ ├── domain.rsa.key
|
|
||||||
│ ├── domain.rsa.pem
|
|
||||||
│ ├── full_chained.pem
|
|
||||||
│ └── session_ticket.key
|
|
||||||
└── tpl # 模版文件
|
|
||||||
├── crossdomainTpl.xml
|
|
||||||
├── feedTpl.xml
|
|
||||||
├── opensearchTpl.xml
|
|
||||||
├── robotsTpl.xml
|
|
||||||
└── sitemapTpl.xml
|
|
||||||
```
|
|
||||||
| 名称 | 描述 |
|
|
||||||
| ----------- | ---------------------------------------- |
|
|
||||||
| app.yml | 整个程序的配置文件,里面已经列出了所有配置项的说明,这里不再阐述。 |
|
|
||||||
| blackip.yml | 如果没有使用 `Nginx`,博客内置 `ip` 过滤系统。 |
|
|
||||||
| 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) 查看,了解详情。 |
|
|
||||||
| scts | 存放 ct 文件。 |
|
|
||||||
| ssl | 这里存放了所有证书相关的内容。 |
|
|
||||||
| tpl | 模版相关,不用修改。 |
|
|
||||||
|
|
||||||
### 开始部署
|
|
||||||
|
|
||||||
#### docker
|
|
||||||
请确定你已经完成了上面所说的所有步骤,在本地已经测试成功。服务器上 `MognoDB` 和`Elasticsearch` 已经安装并已经运行成功。
|
|
||||||
|
|
||||||
首先,请将本地测试好的 `conf` 文件夹上传至服务器,建议存储到服务器 `/data/eiblog` 下。
|
|
||||||
``` sh
|
|
||||||
$ tree /data/eiblog -L 1
|
|
||||||
|
|
||||||
├── conf
|
|
||||||
```
|
|
||||||
|
|
||||||
然后,将镜像 PULL 到服务器本地。
|
|
||||||
``` sh
|
|
||||||
# PULL下Eiblog镜像
|
|
||||||
$ docker pull registry.cn-hangzhou.aliyuncs.com/deepzz/eiblog
|
|
||||||
```
|
|
||||||
|
|
||||||
最后,执行 `docker run` 命令,希望你能成功。
|
|
||||||
``` sh
|
|
||||||
$ docker run -d --name eiblog --restart=always \
|
|
||||||
--add-host disqus.com:23.235.33.134 \
|
|
||||||
--add-host mongodb:172.42.0.1 \
|
|
||||||
--add-host elasticsearch:192.168.99.100 \
|
|
||||||
-p 9000:9000 \
|
|
||||||
-e GODEBUG=netdns=cgo \
|
|
||||||
-v /data/eiblog/logdata:/eiblog/logdata \
|
|
||||||
-v /data/eiblog/conf:/eiblog/conf \
|
|
||||||
registry.cn-hangzhou.aliyuncs.com/deepzz/eiblog
|
|
||||||
```
|
|
||||||
这里默认 `MongDB` 和 `Elasticsearch` 均为 `docker` 部署,且名称为`eidb`,`eisearch`。
|
|
||||||
|
|
||||||
#### nginx + docker
|
|
||||||
通过 `Nginx+docker` 部署,是博主推荐的方式。这里采用 `Docker Compose` 管理我们整个博客系统。
|
|
||||||
|
|
||||||
请确认你已经成功安装好 `Nginx`、`docker`、`docker-compose`。Nginx 请一定参照 Jerry Qu 的[Nginx 配置完整篇](https://imququ.com/post/my-nginx-conf.html)。
|
|
||||||
|
|
||||||
首先,请将本地测试好的 `conf`,`docker-compose.yml` 文件夹和文件上传至服务器。`conf` 建议存储到服务器 `/data/eiblog` 下,`docker-compose.yml` 存放在你使用方便的地方。
|
|
||||||
|
|
||||||
``` sh
|
|
||||||
$ tree /data/eiblog -L 1
|
|
||||||
|
|
||||||
├── conf
|
|
||||||
|
|
||||||
$ ls ~/
|
|
||||||
|
|
||||||
docker-compose.yml
|
|
||||||
```
|
|
||||||
|
|
||||||
然后,执行:
|
|
||||||
``` sh
|
|
||||||
$ cd ~
|
|
||||||
$ docker-compose up -d
|
|
||||||
```
|
|
||||||
|
|
||||||
等待些许时间,成功运行。
|
|
||||||
@@ -1,74 +0,0 @@
|
|||||||
### 郑重提醒
|
|
||||||
**标题**、**slug**、**内容**。在你点击保存的时候一定确保三者不能为空,否则页面刷新内容就没了。所以,养成一个良好的写作习惯很重要。
|
|
||||||
|
|
||||||
当然,博客的自动保存功能也非常的好。在你不确定是否发布前,你可以将之保存到草稿,以便下次继续编辑。
|
|
||||||
|
|
||||||
### 文章标题
|
|
||||||
文章标题,这个可能要看个人习惯。我习惯从三级标题开始(###),依次往下四级标题,五级标题...。要注意的是一定不能跳级:
|
|
||||||
```
|
|
||||||
### 标题一
|
|
||||||
|
|
||||||
#### 标题1.1
|
|
||||||
#### 标题1.2
|
|
||||||
##### 标题1.2.1
|
|
||||||
##### 标题1.2.2
|
|
||||||
|
|
||||||
### 标题二
|
|
||||||
|
|
||||||
##### 标题2.1
|
|
||||||
|
|
||||||
##### 标题2.2
|
|
||||||
###### 标题2.2.1
|
|
||||||
###### 标题2.2.2
|
|
||||||
```
|
|
||||||
|
|
||||||
结果是:
|
|
||||||

|
|
||||||
|
|
||||||
### 文章描述
|
|
||||||
文章描述,主要是给`html->head->meta`中的 name 为 description 用的。现采用了一个临时的办法:在文章的第一行通过前缀识别(只看第一行)。
|
|
||||||
|
|
||||||
该前缀可到`conf/app.yml`设置,默认为`Desc:`,如:
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
### 图片懒加载
|
|
||||||
博客系统提供图片懒加载功能(浏览到某个位置,图片才会加载),以此来提高页面加载速度。我们可根据需要是否使用。
|
|
||||||
|
|
||||||
当然由此带来的坏处就是rss不能够正确加载图片。后续看是否解决这个问题或朋友提PR。
|
|
||||||
|
|
||||||
首先看下图片的`markdown`标准写法:
|
|
||||||
```
|
|
||||||

|
|
||||||
```
|
|
||||||
如:
|
|
||||||
```
|
|
||||||

|
|
||||||
```
|
|
||||||

|
|
||||||
|
|
||||||
懒加载,需要为该图片指定大小(长高):
|
|
||||||
```
|
|
||||||

|
|
||||||
```
|
|
||||||
|
|
||||||
x 为小写字母(x,y,z)中的 x。使页面未加载时也占了相应的位置大小,这样设计是为了让读者在浏览页面时不会感到抖动。
|
|
||||||
|
|
||||||
如:
|
|
||||||
```
|
|
||||||

|
|
||||||
```
|
|
||||||
|
|
||||||
### 摘要截取
|
|
||||||
摘要截取主要是提供给首页显示,如:
|
|
||||||

|
|
||||||
|
|
||||||
红框中圈出来的就是截取出来的内容。在 `conf/app.yml` 的配置项有两个:
|
|
||||||
```
|
|
||||||
# 自动截取预览, 字符数
|
|
||||||
length: 400
|
|
||||||
# 截取预览标识
|
|
||||||
identifier: <!--more-->
|
|
||||||
|
|
||||||
```
|
|
||||||
当程序不能检查到 identifier 的标识符时,会采用长度的方式进行截取。
|
|
||||||
316
elasticsearch.go
@@ -1,316 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"encoding/json"
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"io/ioutil"
|
|
||||||
"net"
|
|
||||||
"net/http"
|
|
||||||
"regexp"
|
|
||||||
"strings"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/eiblog/utils/logd"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
INDEX = "eiblog"
|
|
||||||
TYPE = "article"
|
|
||||||
|
|
||||||
ES_FILTER = `"filter":{"bool":{"must":[%s]}}`
|
|
||||||
ES_TERM = `{"term":{"%s":"%s"}}`
|
|
||||||
ES_DATE = `{"range":{"date":{"gte":"%s","lte": "%s","format": "yyyy-MM-dd||yyyy-MM||yyyy"}}}` // 2016-10||/M
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
ErrUninitializedES = errors.New("uninitialized elasticsearch")
|
|
||||||
|
|
||||||
es *ElasticService
|
|
||||||
)
|
|
||||||
|
|
||||||
// 初始化 Elasticsearch 服务器
|
|
||||||
func init() {
|
|
||||||
_, err := net.LookupIP("elasticsearch")
|
|
||||||
if err != nil {
|
|
||||||
logd.Info(err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
es = &ElasticService{url: "http://elasticsearch:9200", c: new(http.Client)}
|
|
||||||
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)
|
|
||||||
err := CreateIndexAndMappings(INDEX, TYPE, []byte(mappings))
|
|
||||||
if err != nil {
|
|
||||||
logd.Error(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 查询
|
|
||||||
func Elasticsearch(qStr string, size, from int) (*ESSearchResult, error) {
|
|
||||||
if es == nil {
|
|
||||||
return nil, ErrUninitializedES
|
|
||||||
}
|
|
||||||
|
|
||||||
// 分析查询字符串
|
|
||||||
reg := regexp.MustCompile(`(tag|slug|date):`)
|
|
||||||
indexs := reg.FindAllStringIndex(qStr, -1)
|
|
||||||
length := len(indexs)
|
|
||||||
var str, kw string
|
|
||||||
var filter []string
|
|
||||||
if length == 0 { // 全文搜索
|
|
||||||
kw = qStr
|
|
||||||
}
|
|
||||||
// 字段搜索,检出 全文搜索
|
|
||||||
for i, index := range indexs {
|
|
||||||
if i == length-1 {
|
|
||||||
str = qStr[index[0]:]
|
|
||||||
if space := strings.Index(str, " "); space != -1 && space < len(str)-1 {
|
|
||||||
kw = str[space+1:]
|
|
||||||
str = str[:space]
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
str = strings.TrimSpace(qStr[index[0]:indexs[i+1][0]])
|
|
||||||
}
|
|
||||||
kv := strings.Split(str, ":")
|
|
||||||
switch kv[0] {
|
|
||||||
case "slug":
|
|
||||||
filter = append(filter, fmt.Sprintf(ES_TERM, kv[0], kv[1]))
|
|
||||||
case "tag":
|
|
||||||
filter = append(filter, fmt.Sprintf(ES_TERM, kv[0], kv[1]))
|
|
||||||
case "date":
|
|
||||||
var date string
|
|
||||||
switch len(kv[1]) {
|
|
||||||
case 4:
|
|
||||||
date = fmt.Sprintf(ES_DATE, kv[1], kv[1]+"||/y")
|
|
||||||
case 7:
|
|
||||||
date = fmt.Sprintf(ES_DATE, kv[1], kv[1]+"||/M")
|
|
||||||
case 10:
|
|
||||||
date = fmt.Sprintf(ES_DATE, kv[1], kv[1]+"||/d")
|
|
||||||
default:
|
|
||||||
break
|
|
||||||
}
|
|
||||||
filter = append(filter, date)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// 判断是否为空,选择搜索方式
|
|
||||||
var dsl string
|
|
||||||
if kw != "" {
|
|
||||||
dsl = strings.Replace(strings.Replace(`{"highlight":{"fields":{"content":{},"title":{}},"post_tags":["\u003c/b\u003e"],"pre_tags":["\u003cb\u003e"]},"query":{"dis_max":{"queries":[{"match":{"title":{"boost":4,"minimum_should_match":"50%","query":"$1"}}},{"match":{"content":{"boost":4,"minimum_should_match":"75%","query":"$1"}}},{"match":{"tag":{"boost":2,"minimum_should_match":"100%","query":"$1"}}},{"match":{"slug":{"boost":1,"minimum_should_match":"100%","query":"$1"}}}],"tie_breaker":0.3}},$2}`, "$1", kw, -1), "$2", fmt.Sprintf(ES_FILTER, strings.Join(filter, ",")), -1)
|
|
||||||
} else {
|
|
||||||
dsl = fmt.Sprintf("{"+ES_FILTER+"}", strings.Join(filter, ","))
|
|
||||||
}
|
|
||||||
docs, err := IndexQueryDSL(INDEX, TYPE, size, from, []byte(dsl))
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return docs, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// 添加或更新索引
|
|
||||||
func ElasticIndex(artc *Article) error {
|
|
||||||
if es == nil {
|
|
||||||
return ErrUninitializedES
|
|
||||||
}
|
|
||||||
|
|
||||||
img := PickFirstImage(artc.Content)
|
|
||||||
mapping := map[string]interface{}{
|
|
||||||
"title": artc.Title,
|
|
||||||
"content": IgnoreHtmlTag(artc.Content),
|
|
||||||
"slug": artc.Slug,
|
|
||||||
"tag": artc.Tags,
|
|
||||||
"img": img,
|
|
||||||
"date": artc.CreateTime,
|
|
||||||
}
|
|
||||||
b, _ := json.Marshal(mapping)
|
|
||||||
return IndexOrUpdateDocument(INDEX, TYPE, artc.ID, b)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 删除索引
|
|
||||||
func ElasticDelIndex(ids []int32) error {
|
|
||||||
if es == nil {
|
|
||||||
return ErrUninitializedES
|
|
||||||
}
|
|
||||||
|
|
||||||
var target []string
|
|
||||||
for _, id := range ids {
|
|
||||||
target = append(target, fmt.Sprint(id))
|
|
||||||
}
|
|
||||||
return DeleteDocument(INDEX, TYPE, target)
|
|
||||||
}
|
|
||||||
|
|
||||||
///////////////////////////// Elasticsearch api /////////////////////////////
|
|
||||||
type ElasticService struct {
|
|
||||||
c *http.Client
|
|
||||||
url string
|
|
||||||
}
|
|
||||||
|
|
||||||
type IndicesCreateResult struct {
|
|
||||||
Acknowledged bool `json:"acknowledged"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// 返回 url
|
|
||||||
func (s *ElasticService) ParseURL(format string, params ...interface{}) string {
|
|
||||||
return fmt.Sprintf(s.url+format, params...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Elastic 相关操作请求
|
|
||||||
func (s *ElasticService) Do(req *http.Request) (interface{}, error) {
|
|
||||||
resp, err := s.c.Do(req)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
defer resp.Body.Close()
|
|
||||||
switch req.Method {
|
|
||||||
case "POST":
|
|
||||||
fallthrough
|
|
||||||
case "DELETE":
|
|
||||||
fallthrough
|
|
||||||
case "PUT":
|
|
||||||
fallthrough
|
|
||||||
case "GET":
|
|
||||||
b, err := ioutil.ReadAll(resp.Body)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return b, nil
|
|
||||||
case "HEAD":
|
|
||||||
return resp.StatusCode, nil
|
|
||||||
}
|
|
||||||
return nil, errors.New("unknown methods")
|
|
||||||
}
|
|
||||||
|
|
||||||
func CreateIndexAndMappings(index, typ string, mappings []byte) (err error) {
|
|
||||||
req, err := http.NewRequest("HEAD", es.ParseURL("/%s/%s", index, typ), nil)
|
|
||||||
code, err := es.Do(req)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if code.(int) == http.StatusOK {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
req, err = http.NewRequest("PUT", es.ParseURL("/%s", index), bytes.NewReader(mappings))
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
data, err := es.Do(req)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
var rst IndicesCreateResult
|
|
||||||
err = json.Unmarshal(data.([]byte), &rst)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if !rst.Acknowledged {
|
|
||||||
return errors.New(string(data.([]byte)))
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// 创建或更新索引
|
|
||||||
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))
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
data, err := es.Do(req)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
logd.Debug(string(data.([]byte)))
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
type ESDeleteDocument struct {
|
|
||||||
Index string `json:"_index"`
|
|
||||||
Type string `json:"_type"`
|
|
||||||
ID string `json:"_id"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type ESDeleteResult struct {
|
|
||||||
Errors bool `json:"errors"`
|
|
||||||
Iterms []map[string]struct {
|
|
||||||
Error string `json:"error"`
|
|
||||||
} `json:"iterms"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// 删除文档
|
|
||||||
func DeleteDocument(index, typ string, ids []string) error {
|
|
||||||
var buff bytes.Buffer
|
|
||||||
for _, id := range ids {
|
|
||||||
dd := &ESDeleteDocument{Index: index, Type: typ, ID: id}
|
|
||||||
m := map[string]*ESDeleteDocument{"delete": dd}
|
|
||||||
b, _ := json.Marshal(m)
|
|
||||||
buff.Write(b)
|
|
||||||
buff.WriteByte('\n')
|
|
||||||
}
|
|
||||||
req, err := http.NewRequest("POST", es.ParseURL("/_bulk"), bytes.NewReader(buff.Bytes()))
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
data, err := es.Do(req)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
var result ESDeleteResult
|
|
||||||
err = json.Unmarshal(data.([]byte), &result)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if result.Errors {
|
|
||||||
for _, iterm := range result.Iterms {
|
|
||||||
for _, s := range iterm {
|
|
||||||
if s.Error != "" {
|
|
||||||
return errors.New(s.Error)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// 查询结果
|
|
||||||
type ESSearchResult struct {
|
|
||||||
Took float32 `json:"took"`
|
|
||||||
Hits struct {
|
|
||||||
Total int `json:"total"`
|
|
||||||
Hits []struct {
|
|
||||||
ID string `json:"_id"`
|
|
||||||
Source struct {
|
|
||||||
Slug string `json:"slug"`
|
|
||||||
Content string `json:"content"`
|
|
||||||
Date time.Time `json:"date"`
|
|
||||||
Title string `json:"title"`
|
|
||||||
Img string `json:"img"`
|
|
||||||
} `json:"_source"`
|
|
||||||
Highlight struct {
|
|
||||||
Title []string `json:"title"`
|
|
||||||
Content []string `json:"content"`
|
|
||||||
} `json:"highlight"`
|
|
||||||
} `json:"hits"`
|
|
||||||
} `json:"hits"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// DSL 语句查询文档
|
|
||||||
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))
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
data, err := es.Do(req)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
result := &ESSearchResult{}
|
|
||||||
err = json.Unmarshal(data.([]byte), result)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return result, nil
|
|
||||||
}
|
|
||||||
@@ -1,128 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestCreateIndexAndMappings(t *testing.T) {
|
|
||||||
mapping := map[string]interface{}{
|
|
||||||
"mappings": map[string]interface{}{
|
|
||||||
"article": map[string]interface{}{
|
|
||||||
"properties": map[string]interface{}{
|
|
||||||
"title": map[string]string{
|
|
||||||
"type": "string",
|
|
||||||
"term_vector": "with_positions_offsets",
|
|
||||||
"analyzer": "ik_syno",
|
|
||||||
"search_analyzer": "ik_syno",
|
|
||||||
},
|
|
||||||
"content": map[string]string{
|
|
||||||
"type": "string",
|
|
||||||
"term_vector": "with_positions_offsets",
|
|
||||||
"analyzer": "ik_syno",
|
|
||||||
"search_analyzer": "ik_syno",
|
|
||||||
},
|
|
||||||
"slug": map[string]string{
|
|
||||||
"type": "string",
|
|
||||||
},
|
|
||||||
"tags": map[string]string{
|
|
||||||
"type": "string",
|
|
||||||
"index": "not_analyzed",
|
|
||||||
},
|
|
||||||
"update_time": map[string]string{
|
|
||||||
"type": "date",
|
|
||||||
"index": "not_analyzed",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
b, _ := json.Marshal(mapping)
|
|
||||||
err := CreateIndexAndMappings(INDEX, TYPE, b)
|
|
||||||
if err != nil {
|
|
||||||
t.Error(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestIndexDocument(t *testing.T) {
|
|
||||||
mapping := map[string]interface{}{
|
|
||||||
"title": "简单到不知道为什么",
|
|
||||||
"content": `最近有很多朋友邮件或者留言询问本博客服务端配置相关问题,基本都是关于 HTTPS 和 HTTP/2 的,其实我的 Nginx 配置在之前的文章中多次提到过,不过都比较分散。为了方便大家参考,本文贴出完整配置。本文内容会随时调整或更新,请大家不要把本文内容全文转载到第三方平台,以免给他人造成困扰或误导。另外限于篇幅,本文不会对配置做过多说明,如有疑问或不同意见,欢迎留言指出。
|
|
||||||
`,
|
|
||||||
"slug": "vim3",
|
|
||||||
"tags": []string{"js", "javascript", "test"},
|
|
||||||
"update_time": "2015-12-15T13:05:55Z",
|
|
||||||
}
|
|
||||||
b, _ := json.Marshal(mapping)
|
|
||||||
err := IndexOrUpdateDocument(INDEX, TYPE, int32(11), b)
|
|
||||||
if err != nil {
|
|
||||||
t.Error(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestIndexQueryDSL(t *testing.T) {
|
|
||||||
kw := "实现访问限制"
|
|
||||||
dsl := map[string]interface{}{
|
|
||||||
"query": map[string]interface{}{
|
|
||||||
"dis_max": map[string]interface{}{
|
|
||||||
"queries": []map[string]interface{}{
|
|
||||||
map[string]interface{}{
|
|
||||||
"match": map[string]interface{}{
|
|
||||||
"title": map[string]interface{}{
|
|
||||||
"query": kw,
|
|
||||||
"minimum_should_match": "50%",
|
|
||||||
"boost": 4,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
map[string]interface{}{
|
|
||||||
"match": map[string]interface{}{
|
|
||||||
"content": map[string]interface{}{
|
|
||||||
"query": kw,
|
|
||||||
"minimum_should_match": "75%",
|
|
||||||
"boost": 4,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
map[string]interface{}{
|
|
||||||
"match": map[string]interface{}{
|
|
||||||
"tags": map[string]interface{}{
|
|
||||||
"query": kw,
|
|
||||||
"minimum_should_match": "100%",
|
|
||||||
"boost": 2,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
map[string]interface{}{
|
|
||||||
"match": map[string]interface{}{
|
|
||||||
"slug": map[string]interface{}{
|
|
||||||
"query": kw,
|
|
||||||
"minimum_should_match": "100%",
|
|
||||||
"boost": 1,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
"tie_breaker": 0.3,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
"highlight": map[string]interface{}{
|
|
||||||
"pre_tags": []string{"<b>"},
|
|
||||||
"post_tags": []string{"</b>"},
|
|
||||||
"fields": map[string]interface{}{
|
|
||||||
"title": map[string]string{},
|
|
||||||
"content": map[string]string{
|
|
||||||
// "fragment_size": 150,
|
|
||||||
// "number_of_fragments": "3",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
b, _ := json.Marshal(dsl)
|
|
||||||
fmt.Println(string(b))
|
|
||||||
_, err := IndexQueryDSL(INDEX, TYPE, 10, 1, b)
|
|
||||||
if err != nil {
|
|
||||||
t.Error(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
1
examples/README.md
Normal file
@@ -0,0 +1 @@
|
|||||||
|
Examples for your applications and/or public libraries.
|
||||||
457
front.go
@@ -1,457 +0,0 @@
|
|||||||
// Package main provides ...
|
|
||||||
// 这里是前端页面展示相关接口
|
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"fmt"
|
|
||||||
"html/template"
|
|
||||||
"io/ioutil"
|
|
||||||
"net/http"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/eiblog/eiblog/setting"
|
|
||||||
"github.com/eiblog/utils/logd"
|
|
||||||
"github.com/gin-gonic/gin"
|
|
||||||
)
|
|
||||||
|
|
||||||
func Filter() gin.HandlerFunc {
|
|
||||||
return func(c *gin.Context) {
|
|
||||||
// 过滤黑名单
|
|
||||||
if BlackFilter(c) {
|
|
||||||
c.Abort()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
// 重定向
|
|
||||||
if Redirect(c) {
|
|
||||||
c.Abort()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
// 用户cookie,用于统计
|
|
||||||
UserCookie(c)
|
|
||||||
c.Next()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 黑名单过滤
|
|
||||||
func BlackFilter(c *gin.Context) bool {
|
|
||||||
ip := c.ClientIP()
|
|
||||||
if setting.BlackIP[ip] {
|
|
||||||
c.String(http.StatusForbidden, "Your IP is blacklisted.")
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// 重定向
|
|
||||||
func Redirect(c *gin.Context) bool {
|
|
||||||
if setting.Conf.Mode.EnableHttps && c.Request.ProtoMajor == 1 {
|
|
||||||
var port string
|
|
||||||
if strings.Contains(c.Request.Host, ":") {
|
|
||||||
port = fmt.Sprintf(":%d", setting.Conf.Mode.HttpsPort)
|
|
||||||
}
|
|
||||||
c.Redirect(http.StatusMovedPermanently, "https://"+setting.Conf.Mode.Domain+port+c.Request.RequestURI)
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// 用户识别
|
|
||||||
func UserCookie(c *gin.Context) {
|
|
||||||
cookie, err := c.Cookie("u")
|
|
||||||
if err != nil || cookie == "" {
|
|
||||||
c.SetCookie("u", RandUUIDv4(), 86400*730, "/", "", true, true)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 解析静态文件版本
|
|
||||||
func StaticVersion(c *gin.Context) (version int) {
|
|
||||||
cookie, err := c.Request.Cookie("v")
|
|
||||||
if err != nil || cookie.Value != fmt.Sprint(setting.Conf.StaticVersion) {
|
|
||||||
return setting.Conf.StaticVersion
|
|
||||||
}
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
func GetBase() gin.H {
|
|
||||||
return gin.H{
|
|
||||||
"BlogName": Ei.BlogName,
|
|
||||||
"SubTitle": Ei.SubTitle,
|
|
||||||
"Twitter": setting.Conf.Twitter,
|
|
||||||
"CopyYear": time.Now().Year(),
|
|
||||||
"BTitle": Ei.BTitle,
|
|
||||||
"BeiAn": Ei.BeiAn,
|
|
||||||
"Domain": setting.Conf.Mode.Domain,
|
|
||||||
"Qiniu": setting.Conf.Qiniu,
|
|
||||||
"Disqus": setting.Conf.Disqus,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// not found
|
|
||||||
func HandleNotFound(c *gin.Context) {
|
|
||||||
h := GetBase()
|
|
||||||
h["Version"] = StaticVersion(c)
|
|
||||||
h["Title"] = "Not Found"
|
|
||||||
h["Description"] = "404 Not Found"
|
|
||||||
h["Path"] = ""
|
|
||||||
c.Status(http.StatusNotFound)
|
|
||||||
RenderHTMLFront(c, "notfound", h)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 首页
|
|
||||||
func HandleHomePage(c *gin.Context) {
|
|
||||||
h := GetBase()
|
|
||||||
h["Version"] = StaticVersion(c)
|
|
||||||
h["Title"] = Ei.BTitle + " | " + Ei.SubTitle
|
|
||||||
h["Description"] = "博客首页," + Ei.SubTitle
|
|
||||||
h["Path"] = c.Request.URL.Path
|
|
||||||
h["CurrentPage"] = "blog-home"
|
|
||||||
pn, err := strconv.Atoi(c.Query("pn"))
|
|
||||||
if err != nil || pn < 1 {
|
|
||||||
pn = 1
|
|
||||||
}
|
|
||||||
h["Prev"], h["Next"], h["List"] = PageList(pn, setting.Conf.General.PageNum)
|
|
||||||
c.Status(http.StatusOK)
|
|
||||||
RenderHTMLFront(c, "home", h)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 专题页
|
|
||||||
func HandleSeriesPage(c *gin.Context) {
|
|
||||||
h := GetBase()
|
|
||||||
h["Version"] = StaticVersion(c)
|
|
||||||
h["Title"] = "专题 | " + Ei.BTitle
|
|
||||||
h["Description"] = "专题列表," + Ei.SubTitle
|
|
||||||
h["Path"] = c.Request.URL.Path
|
|
||||||
h["CurrentPage"] = "series"
|
|
||||||
h["Article"] = Ei.PageSeries
|
|
||||||
c.Status(http.StatusOK)
|
|
||||||
RenderHTMLFront(c, "series", h)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 归档页
|
|
||||||
func HandleArchivesPage(c *gin.Context) {
|
|
||||||
h := GetBase()
|
|
||||||
h["Version"] = StaticVersion(c)
|
|
||||||
h["Title"] = "归档 | " + Ei.BTitle
|
|
||||||
h["Description"] = "博客归档," + Ei.SubTitle
|
|
||||||
h["Path"] = c.Request.URL.Path
|
|
||||||
h["CurrentPage"] = "archives"
|
|
||||||
h["Article"] = Ei.PageArchives
|
|
||||||
c.Status(http.StatusOK)
|
|
||||||
RenderHTMLFront(c, "archives", h)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 文章
|
|
||||||
func HandleArticlePage(c *gin.Context) {
|
|
||||||
path := c.Param("slug")
|
|
||||||
if !strings.HasSuffix(path, ".html") || Ei.MapArticles[path[:len(path)-5]] == nil {
|
|
||||||
HandleNotFound(c)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
artc := Ei.MapArticles[path[:len(path)-5]]
|
|
||||||
h := GetBase()
|
|
||||||
h["Version"] = StaticVersion(c)
|
|
||||||
h["Title"] = artc.Title + " | " + Ei.BTitle
|
|
||||||
h["Path"] = c.Request.URL.Path
|
|
||||||
h["CurrentPage"] = "post-" + artc.Slug
|
|
||||||
var name string
|
|
||||||
if path == "blogroll.html" {
|
|
||||||
name = "blogroll"
|
|
||||||
h["Description"] = "友情连接," + Ei.SubTitle
|
|
||||||
} else if path == "about.html" {
|
|
||||||
name = "about"
|
|
||||||
h["Description"] = "关于作者," + Ei.SubTitle
|
|
||||||
} else {
|
|
||||||
h["Description"] = artc.Desc + "," + Ei.SubTitle
|
|
||||||
name = "article"
|
|
||||||
h["Copyright"] = Ei.Copyright
|
|
||||||
if !artc.UpdateTime.IsZero() {
|
|
||||||
h["Days"] = int(time.Now().Sub(artc.UpdateTime).Hours()) / 24
|
|
||||||
} else {
|
|
||||||
h["Days"] = int(time.Now().Sub(artc.CreateTime).Hours()) / 24
|
|
||||||
}
|
|
||||||
if artc.SerieID > 0 {
|
|
||||||
h["Serie"] = QuerySerie(artc.SerieID)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
h["Article"] = artc
|
|
||||||
c.Status(http.StatusOK)
|
|
||||||
RenderHTMLFront(c, name, h)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 搜索页
|
|
||||||
func HandleSearchPage(c *gin.Context) {
|
|
||||||
h := GetBase()
|
|
||||||
h["Version"] = StaticVersion(c)
|
|
||||||
h["Title"] = "站内搜索 | " + Ei.BTitle
|
|
||||||
h["Description"] = "站内搜索," + Ei.SubTitle
|
|
||||||
h["Path"] = ""
|
|
||||||
h["CurrentPage"] = "search-post"
|
|
||||||
|
|
||||||
q := strings.TrimSpace(c.Query("q"))
|
|
||||||
if q != "" {
|
|
||||||
start, err := strconv.Atoi(c.Query("start"))
|
|
||||||
if start < 1 || err != nil {
|
|
||||||
start = 1
|
|
||||||
}
|
|
||||||
h["Word"] = q
|
|
||||||
|
|
||||||
vals := c.Request.URL.Query()
|
|
||||||
result, err := Elasticsearch(q, setting.Conf.General.PageNum, start-1)
|
|
||||||
if err != nil {
|
|
||||||
logd.Error(err)
|
|
||||||
} else {
|
|
||||||
result.Took /= 1000
|
|
||||||
for i, v := range result.Hits.Hits {
|
|
||||||
if artc := Ei.MapArticles[result.Hits.Hits[i].Source.Slug]; len(v.Highlight.Content) == 0 && artc != nil {
|
|
||||||
result.Hits.Hits[i].Highlight.Content = []string{artc.Excerpt}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
h["SearchResult"] = result
|
|
||||||
if start-setting.Conf.General.PageNum > 0 {
|
|
||||||
vals.Set("start", fmt.Sprint(start-setting.Conf.General.PageNum))
|
|
||||||
h["Prev"] = vals.Encode()
|
|
||||||
}
|
|
||||||
if result.Hits.Total >= start+setting.Conf.General.PageNum {
|
|
||||||
vals.Set("start", fmt.Sprint(start+setting.Conf.General.PageNum))
|
|
||||||
h["Next"] = vals.Encode()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
h["HotWords"] = setting.Conf.HotWords
|
|
||||||
}
|
|
||||||
c.Status(http.StatusOK)
|
|
||||||
RenderHTMLFront(c, "search", h)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 评论页
|
|
||||||
func HandleDisqusFrom(c *gin.Context) {
|
|
||||||
params := strings.Split(c.Param("slug"), "|")
|
|
||||||
if len(params) != 4 || params[1] == "" {
|
|
||||||
c.String(http.StatusOK, "出错啦。。。")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
artc := Ei.MapArticles[params[0]]
|
|
||||||
data := gin.H{
|
|
||||||
"Title": "发表评论 | " + Ei.BTitle,
|
|
||||||
"ATitle": artc.Title,
|
|
||||||
"Thread": params[1],
|
|
||||||
"Slug": artc.Slug,
|
|
||||||
}
|
|
||||||
err := Tmpl.ExecuteTemplate(c.Writer, "disqus.html", data)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
c.Header("Content-Type", "text/html; charset=utf-8")
|
|
||||||
}
|
|
||||||
|
|
||||||
// feed
|
|
||||||
func HandleFeed(c *gin.Context) {
|
|
||||||
http.ServeFile(c.Writer, c.Request, "static/feed.xml")
|
|
||||||
}
|
|
||||||
|
|
||||||
// opensearch
|
|
||||||
func HandleOpenSearch(c *gin.Context) {
|
|
||||||
http.ServeFile(c.Writer, c.Request, "static/opensearch.xml")
|
|
||||||
}
|
|
||||||
|
|
||||||
// robots
|
|
||||||
func HandleRobots(c *gin.Context) {
|
|
||||||
http.ServeFile(c.Writer, c.Request, "static/robots.txt")
|
|
||||||
}
|
|
||||||
|
|
||||||
// sitemap
|
|
||||||
func HandleSitemap(c *gin.Context) {
|
|
||||||
http.ServeFile(c.Writer, c.Request, "static/sitemap.xml")
|
|
||||||
}
|
|
||||||
|
|
||||||
// cross domain
|
|
||||||
func HandleCrossDomain(c *gin.Context) {
|
|
||||||
http.ServeFile(c.Writer, c.Request, "static/crossdomain.xml")
|
|
||||||
}
|
|
||||||
|
|
||||||
// favicon
|
|
||||||
func HandleFavicon(c *gin.Context) {
|
|
||||||
http.ServeFile(c.Writer, c.Request, "static/favicon.ico")
|
|
||||||
}
|
|
||||||
|
|
||||||
// 服务端推送谷歌统计
|
|
||||||
func HandleBeacon(c *gin.Context) {
|
|
||||||
ua := c.Request.UserAgent()
|
|
||||||
// TODO 过滤黑名单
|
|
||||||
vals := c.Request.URL.Query()
|
|
||||||
vals.Set("v", setting.Conf.Google.V)
|
|
||||||
vals.Set("tid", setting.Conf.Google.Tid)
|
|
||||||
vals.Set("t", setting.Conf.Google.T)
|
|
||||||
cookie, _ := c.Cookie("u")
|
|
||||||
vals.Set("cid", cookie)
|
|
||||||
|
|
||||||
vals.Set("dl", c.Request.Referer())
|
|
||||||
vals.Set("uip", c.ClientIP())
|
|
||||||
go func() {
|
|
||||||
req, err := http.NewRequest("POST", setting.Conf.Google.URL, strings.NewReader(vals.Encode()))
|
|
||||||
if err != nil {
|
|
||||||
logd.Error(err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
req.Header.Set("User-Agent", ua)
|
|
||||||
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
|
|
||||||
res, err := http.DefaultClient.Do(req)
|
|
||||||
if err != nil {
|
|
||||||
logd.Error(err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
defer res.Body.Close()
|
|
||||||
data, err := ioutil.ReadAll(res.Body)
|
|
||||||
if err != nil {
|
|
||||||
logd.Error(err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if res.StatusCode/100 != 2 {
|
|
||||||
logd.Error(string(data))
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
c.String(http.StatusNoContent, "accepted")
|
|
||||||
}
|
|
||||||
|
|
||||||
// 服务端获取评论详细
|
|
||||||
type DisqusComments struct {
|
|
||||||
ErrNo int `json:"errno"`
|
|
||||||
ErrMsg string `json:"errmsg"`
|
|
||||||
Data struct {
|
|
||||||
Next string `json:"next"`
|
|
||||||
Total int `json:"total"`
|
|
||||||
Comments []commentsDetail `json:"comments"`
|
|
||||||
Thread string `json:"thread"`
|
|
||||||
} `json:"data"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type commentsDetail struct {
|
|
||||||
Id string `json:"id"`
|
|
||||||
Parent int `json:"parent"`
|
|
||||||
Name string `json:"name"`
|
|
||||||
Url string `json:"url"`
|
|
||||||
Avatar string `json:"avatar"`
|
|
||||||
CreatedAtStr string `json:"createdAtStr"`
|
|
||||||
Message string `json:"message"`
|
|
||||||
IsDeleted bool `json:"isDeleted"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func HandleDisqus(c *gin.Context) {
|
|
||||||
slug := c.Param("slug")
|
|
||||||
cursor := c.Query("cursor")
|
|
||||||
|
|
||||||
dcs := DisqusComments{}
|
|
||||||
if artc := Ei.MapArticles[slug]; artc != nil {
|
|
||||||
dcs.Data.Thread = artc.Thread
|
|
||||||
}
|
|
||||||
postsList, err := PostsList(slug, cursor)
|
|
||||||
if err != nil {
|
|
||||||
logd.Error(err)
|
|
||||||
dcs.ErrNo = FAIL
|
|
||||||
dcs.ErrMsg = "系统错误"
|
|
||||||
} else {
|
|
||||||
dcs.ErrNo = postsList.Code
|
|
||||||
if postsList.Cursor.HasNext {
|
|
||||||
dcs.Data.Next = postsList.Cursor.Next
|
|
||||||
}
|
|
||||||
dcs.Data.Total = len(postsList.Response)
|
|
||||||
dcs.Data.Comments = make([]commentsDetail, len(postsList.Response))
|
|
||||||
for i, v := range postsList.Response {
|
|
||||||
if dcs.Data.Thread == "" {
|
|
||||||
dcs.Data.Thread = v.Thread
|
|
||||||
}
|
|
||||||
dcs.Data.Comments[i] = commentsDetail{
|
|
||||||
Id: v.Id,
|
|
||||||
Name: v.Author.Name,
|
|
||||||
Parent: v.Parent,
|
|
||||||
Url: v.Author.ProfileUrl,
|
|
||||||
Avatar: v.Author.Avatar.Cache,
|
|
||||||
CreatedAtStr: ConvertStr(v.CreatedAt),
|
|
||||||
Message: v.Message,
|
|
||||||
IsDeleted: v.IsDeleted,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
c.JSON(http.StatusOK, dcs)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 发表评论
|
|
||||||
// [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) {
|
|
||||||
resp := &DisqusCreate{}
|
|
||||||
defer c.JSON(http.StatusOK, resp)
|
|
||||||
|
|
||||||
msg := c.PostForm("message")
|
|
||||||
email := c.PostForm("author_email")
|
|
||||||
name := c.PostForm("author_name")
|
|
||||||
thread := c.PostForm("thread")
|
|
||||||
identifier := c.PostForm("identifier")
|
|
||||||
if msg == "" || email == "" || name == "" || thread == "" || identifier == "" {
|
|
||||||
resp.ErrNo = FAIL
|
|
||||||
resp.ErrMsg = "参数错误"
|
|
||||||
return
|
|
||||||
}
|
|
||||||
fmt.Println("disqus: author: ", email)
|
|
||||||
pc := &PostComment{
|
|
||||||
Message: msg,
|
|
||||||
Parent: c.PostForm("parent"),
|
|
||||||
Thread: thread,
|
|
||||||
AuthorEmail: email,
|
|
||||||
AuthorName: name,
|
|
||||||
Identifier: identifier,
|
|
||||||
IpAddress: c.ClientIP(),
|
|
||||||
}
|
|
||||||
|
|
||||||
postDetail, err := PostCreate(pc)
|
|
||||||
if err != nil {
|
|
||||||
logd.Error(err)
|
|
||||||
resp.ErrNo = FAIL
|
|
||||||
resp.ErrMsg = "系统错误"
|
|
||||||
return
|
|
||||||
}
|
|
||||||
err = PostApprove(postDetail.Response.Id)
|
|
||||||
if err != nil {
|
|
||||||
logd.Error(err)
|
|
||||||
resp.ErrNo = FAIL
|
|
||||||
resp.ErrMsg = "系统错误"
|
|
||||||
return
|
|
||||||
}
|
|
||||||
resp.ErrNo = SUCCESS
|
|
||||||
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) {
|
|
||||||
var buf bytes.Buffer
|
|
||||||
err := Tmpl.ExecuteTemplate(&buf, name, data)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
data["LayoutContent"] = template.HTML(buf.String())
|
|
||||||
err = Tmpl.ExecuteTemplate(c.Writer, "homeLayout.html", data)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
c.Header("Content-Type", "text/html; charset=utf-8")
|
|
||||||
}
|
|
||||||
41
go.mod
@@ -1,28 +1,25 @@
|
|||||||
module github.com/eiblog/eiblog
|
module github.com/eiblog/eiblog/v2
|
||||||
|
|
||||||
|
go 1.15
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/boj/redistore v0.0.0-20180917114910-cd5dcc76aeff // indirect
|
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751
|
||||||
github.com/deepzz0/logd v0.0.0-20171206094927-f91dd8c6316f
|
github.com/deepzz0/logd v0.0.0-20171206094927-f91dd8c6316f
|
||||||
github.com/eiblog/blackfriday v0.0.0-20161010144836-c0ec111761ae
|
github.com/eiblog/blackfriday v0.0.0-20161010144836-c0ec111761ae
|
||||||
|
github.com/eiblog/eiblog v1.4.11
|
||||||
github.com/eiblog/utils v0.0.0-20181119015747-92c93e218753
|
github.com/eiblog/utils v0.0.0-20181119015747-92c93e218753
|
||||||
github.com/gin-contrib/sse v0.1.0 // indirect
|
github.com/gin-contrib/sessions v0.0.3
|
||||||
github.com/gin-gonic/autotls v0.0.0-20180426091246-be87bd5ef97b
|
github.com/gin-gonic/gin v1.6.3
|
||||||
github.com/gin-gonic/contrib v0.0.0-20180614032058-39cfb9727134
|
github.com/gofrs/uuid v3.3.0+incompatible
|
||||||
github.com/gin-gonic/gin v1.3.0
|
github.com/golang/protobuf v1.4.2
|
||||||
github.com/golang/protobuf v1.4.3 // indirect
|
github.com/qiniu/api.v7 v7.2.4+incompatible
|
||||||
github.com/gorilla/sessions v1.2.1 // indirect
|
github.com/qiniu/api.v7/v7 v7.8.2
|
||||||
github.com/json-iterator/go v1.1.10 // indirect
|
github.com/sirupsen/logrus v1.4.2
|
||||||
github.com/mattn/go-isatty v0.0.12 // indirect
|
github.com/swaggo/gin-swagger v1.3.0
|
||||||
github.com/qiniu/api.v7/v7 v7.8.0
|
github.com/swaggo/swag v1.6.9
|
||||||
github.com/shurcooL/sanitized_anchor_name v1.0.0 // indirect
|
google.golang.org/grpc v1.35.0
|
||||||
github.com/stretchr/testify v1.3.0
|
google.golang.org/protobuf v1.25.0
|
||||||
github.com/ugorji/go v1.2.2 // indirect
|
gopkg.in/yaml.v2 v2.3.0
|
||||||
golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad // indirect
|
gorm.io/driver/postgres v1.0.5
|
||||||
golang.org/x/net v0.0.0-20201216054612-986b41b23924 // indirect
|
gorm.io/gorm v1.20.5
|
||||||
gopkg.in/go-playground/assert.v1 v1.2.1 // indirect
|
|
||||||
gopkg.in/go-playground/validator.v8 v8.18.2 // indirect
|
|
||||||
gopkg.in/mgo.v2 v2.0.0-20190816093944-a6b53ec6cb22 // indirect
|
|
||||||
gopkg.in/yaml.v2 v2.2.1
|
|
||||||
)
|
)
|
||||||
|
|
||||||
go 1.13
|
|
||||||
|
|||||||
395
go.sum
@@ -1,6 +1,28 @@
|
|||||||
|
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||||
|
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||||
|
github.com/KyleBanks/depth v1.2.1 h1:5h8fQADFrWtarTdtDudMmGsC7GPbOAu6RVB3ffsVFHc=
|
||||||
|
github.com/KyleBanks/depth v1.2.1/go.mod h1:jzSb9d0L43HxTQfT+oSA1EEp2q+ne2uh6XgeJcm8brE=
|
||||||
|
github.com/PuerkitoBio/purell v1.1.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
|
||||||
|
github.com/PuerkitoBio/purell v1.1.1 h1:WEQqlqaGbrPkxLJWfBwQmfEAE1Z7ONdDLqrN38tNFfI=
|
||||||
|
github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
|
||||||
|
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 h1:d+Bc7a5rLufV/sSk/8dngufqelfh6jnri85riMAaF/M=
|
||||||
|
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
|
||||||
|
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751 h1:JYp7IbQjafoB+tBA3gMyHYHrpOtNuDiK/uB5uXxq5wM=
|
||||||
|
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
||||||
|
github.com/boj/redistore v0.0.0-20180825063928-0920d8493e7f/go.mod h1:+RTT1BOk5P97fT2CiHkbFQwkK3mjsFAP6zCYV2aXtjw=
|
||||||
github.com/boj/redistore v0.0.0-20180917114910-cd5dcc76aeff h1:RmdPFa+slIr4SCBg4st/l/vZWVe9QJKMXGO60Bxbe04=
|
github.com/boj/redistore v0.0.0-20180917114910-cd5dcc76aeff h1:RmdPFa+slIr4SCBg4st/l/vZWVe9QJKMXGO60Bxbe04=
|
||||||
github.com/boj/redistore v0.0.0-20180917114910-cd5dcc76aeff/go.mod h1:+RTT1BOk5P97fT2CiHkbFQwkK3mjsFAP6zCYV2aXtjw=
|
github.com/boj/redistore v0.0.0-20180917114910-cd5dcc76aeff/go.mod h1:+RTT1BOk5P97fT2CiHkbFQwkK3mjsFAP6zCYV2aXtjw=
|
||||||
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
|
github.com/bradfitz/gomemcache v0.0.0-20190329173943-551aad21a668/go.mod h1:H0wQNHz2YrLsuXOZozoeDmnHXkNCRmMW0gwFWDfEZDA=
|
||||||
|
github.com/bradleypeabody/gorilla-sessions-memcache v0.0.0-20181103040241-659414f458e1/go.mod h1:dkChI7Tbtx7H1Tj7TqGSZMOeGpMP5gLHtjroHd4agiI=
|
||||||
|
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||||
|
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||||
|
github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
|
||||||
|
github.com/cockroachdb/apd v1.1.0 h1:3LFP3629v+1aKXU5Q37mxmRxX/pIu1nijXydLShEq5I=
|
||||||
|
github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ=
|
||||||
|
github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
|
||||||
|
github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
|
||||||
|
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
|
||||||
|
github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY=
|
||||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
@@ -8,92 +30,391 @@ github.com/deepzz0/logd v0.0.0-20171206094927-f91dd8c6316f h1:hjWy8ptp0ggYgv/3A8
|
|||||||
github.com/deepzz0/logd v0.0.0-20171206094927-f91dd8c6316f/go.mod h1:8jMj6ab9czIU5udq3ovaK9/5sCIyQ1JWteFMn8w2QRI=
|
github.com/deepzz0/logd v0.0.0-20171206094927-f91dd8c6316f/go.mod h1:8jMj6ab9czIU5udq3ovaK9/5sCIyQ1JWteFMn8w2QRI=
|
||||||
github.com/eiblog/blackfriday v0.0.0-20161010144836-c0ec111761ae h1:V6YC640Gs5jEUYfCimyuXsTW5gzNcIEESG4MGmOJCtA=
|
github.com/eiblog/blackfriday v0.0.0-20161010144836-c0ec111761ae h1:V6YC640Gs5jEUYfCimyuXsTW5gzNcIEESG4MGmOJCtA=
|
||||||
github.com/eiblog/blackfriday v0.0.0-20161010144836-c0ec111761ae/go.mod h1:HzHqTCGEAkSSzBM3shBvQHsHRQYUvjNOIC4mHipZ6tI=
|
github.com/eiblog/blackfriday v0.0.0-20161010144836-c0ec111761ae/go.mod h1:HzHqTCGEAkSSzBM3shBvQHsHRQYUvjNOIC4mHipZ6tI=
|
||||||
|
github.com/eiblog/eiblog v1.4.11 h1:1wpC3YqYKxjwMSuyT8nDJ2Aa5naBhy1gsAkAafSXWbw=
|
||||||
|
github.com/eiblog/eiblog v1.4.11/go.mod h1:t/eol2mrzWp6H86P0FRocRpSux/IfaMGg2fTgc/KYcw=
|
||||||
github.com/eiblog/utils v0.0.0-20181119015747-92c93e218753 h1:Nygjtnh1nF5zejJF7pZnsoFh77wOPS+jlfhikjkJg60=
|
github.com/eiblog/utils v0.0.0-20181119015747-92c93e218753 h1:Nygjtnh1nF5zejJF7pZnsoFh77wOPS+jlfhikjkJg60=
|
||||||
github.com/eiblog/utils v0.0.0-20181119015747-92c93e218753/go.mod h1:mZHWnipRp41yw/rti2DgpbMiBE5i6ifqg7PKooEwRh4=
|
github.com/eiblog/utils v0.0.0-20181119015747-92c93e218753/go.mod h1:mZHWnipRp41yw/rti2DgpbMiBE5i6ifqg7PKooEwRh4=
|
||||||
|
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||||
|
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||||
|
github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
|
||||||
|
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
||||||
|
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
||||||
|
github.com/gin-contrib/gzip v0.0.1 h1:ezvKOL6jH+jlzdHNE4h9h8q8uMpDQjyl0NN0Jd7jozc=
|
||||||
|
github.com/gin-contrib/gzip v0.0.1/go.mod h1:fGBJBCdt6qCZuCAOwWuFhBB4OOq9EFqlo5dEaFhhu5w=
|
||||||
|
github.com/gin-contrib/sessions v0.0.3 h1:PoBXki+44XdJdlgDqDrY5nDVe3Wk7wDV/UCOuLP6fBI=
|
||||||
|
github.com/gin-contrib/sessions v0.0.3/go.mod h1:8C/J6cad3Il1mWYYgtw0w+hqasmpvy25mPkXdOgeB9I=
|
||||||
|
github.com/gin-contrib/sse v0.0.0-20170109093832-22d885f9ecc7/go.mod h1:VJ0WA2NBN22VlZ2dKZQPAPnyWw5XTlK1KymzLKsr59s=
|
||||||
|
github.com/gin-contrib/sse v0.0.0-20190301062529-5545eab6dad3/go.mod h1:VJ0WA2NBN22VlZ2dKZQPAPnyWw5XTlK1KymzLKsr59s=
|
||||||
github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
|
github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
|
||||||
github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
|
github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
|
||||||
github.com/gin-gonic/autotls v0.0.0-20180426091246-be87bd5ef97b h1:dm/NYytoj7p8Jc6zMvyRz3PCQrTTCXnVRvEzyBcM890=
|
|
||||||
github.com/gin-gonic/autotls v0.0.0-20180426091246-be87bd5ef97b/go.mod h1:vwfeXwKgEIWq63oVfwaBjoByS4dZzYbHHROHjV4IjNY=
|
github.com/gin-gonic/autotls v0.0.0-20180426091246-be87bd5ef97b/go.mod h1:vwfeXwKgEIWq63oVfwaBjoByS4dZzYbHHROHjV4IjNY=
|
||||||
github.com/gin-gonic/contrib v0.0.0-20180614032058-39cfb9727134 h1:xgqFZVwmmtWiuq5LUZ/wa34hJR2Dm9NZAH+Cj9a7Hu0=
|
|
||||||
github.com/gin-gonic/contrib v0.0.0-20180614032058-39cfb9727134/go.mod h1:iqneQ2Df3omzIVTkIfn7c1acsVnMGiSLn4XF5Blh3Yg=
|
github.com/gin-gonic/contrib v0.0.0-20180614032058-39cfb9727134/go.mod h1:iqneQ2Df3omzIVTkIfn7c1acsVnMGiSLn4XF5Blh3Yg=
|
||||||
github.com/gin-gonic/gin v1.3.0 h1:kCmZyPklC0gVdL728E6Aj20uYBJV93nj/TkwBTKhFbs=
|
|
||||||
github.com/gin-gonic/gin v1.3.0/go.mod h1:7cKuhb5qV2ggCFctp2fJQ+ErvciLZrIeoOSOm6mUr7Y=
|
github.com/gin-gonic/gin v1.3.0/go.mod h1:7cKuhb5qV2ggCFctp2fJQ+ErvciLZrIeoOSOm6mUr7Y=
|
||||||
|
github.com/gin-gonic/gin v1.4.0/go.mod h1:OW2EZn3DO8Ln9oIKOvM++LBO+5UPHJJDH72/q/3rZdM=
|
||||||
|
github.com/gin-gonic/gin v1.5.0/go.mod h1:Nd6IXA8m5kNZdNEHMBd93KT+mdY3+bewLgRvmCsR2Do=
|
||||||
|
github.com/gin-gonic/gin v1.6.3 h1:ahKqKTFpO5KTPHxWZjEdPScmYaGtLo8Y4DMHoEsnp14=
|
||||||
|
github.com/gin-gonic/gin v1.6.3/go.mod h1:75u5sXoLsGZoRN5Sgbi1eraJ4GU3++wFwWzhwvtwp4M=
|
||||||
|
github.com/globalsign/mgo v0.0.0-20181015135952-eeefdecb41b8/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q=
|
||||||
|
github.com/go-openapi/jsonpointer v0.17.0/go.mod h1:cOnomiV+CVVwFLk0A/MExoFMjwdsUdVpsRhURCKh+3M=
|
||||||
|
github.com/go-openapi/jsonpointer v0.19.3 h1:gihV7YNZK1iK6Tgwwsxo2rJbD1GTbdm72325Bq8FI3w=
|
||||||
|
github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
|
||||||
|
github.com/go-openapi/jsonreference v0.17.0/go.mod h1:g4xxGn04lDIRh0GJb5QlpE3HfopLOL6uZrK/VgnsK9I=
|
||||||
|
github.com/go-openapi/jsonreference v0.19.0/go.mod h1:g4xxGn04lDIRh0GJb5QlpE3HfopLOL6uZrK/VgnsK9I=
|
||||||
|
github.com/go-openapi/jsonreference v0.19.4 h1:3Vw+rh13uq2JFNxgnMTGE1rnoieU9FmyE1gvnyylsYg=
|
||||||
|
github.com/go-openapi/jsonreference v0.19.4/go.mod h1:RdybgQwPxbL4UEjuAruzK1x3nE69AqPYEJeo/TWfEeg=
|
||||||
|
github.com/go-openapi/spec v0.19.0/go.mod h1:XkF/MOi14NmjsfZ8VtAKf8pIlbZzyoTvZsdfssdxcBI=
|
||||||
|
github.com/go-openapi/spec v0.19.9 h1:9z9cbFuZJ7AcvOHKIY+f6Aevb4vObNDkTEyoMfO7rAc=
|
||||||
|
github.com/go-openapi/spec v0.19.9/go.mod h1:vqK/dIdLGCosfvYsQV3WfC7N3TiZSnGY2RZKoFK7X28=
|
||||||
|
github.com/go-openapi/swag v0.17.0/go.mod h1:AByQ+nYG6gQg71GINrmuDXCPWdL640yX49/kXLo40Tg=
|
||||||
|
github.com/go-openapi/swag v0.19.5 h1:lTz6Ys4CmqqCQmZPBlbQENR1/GucA2bzYTE12Pw4tFY=
|
||||||
|
github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
|
||||||
|
github.com/go-playground/assert/v2 v2.0.1 h1:MsBgLAaY856+nPRTKrp3/OZK38U/wa0CcBYNjji3q3A=
|
||||||
|
github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
|
||||||
|
github.com/go-playground/locales v0.12.1/go.mod h1:IUMDtCfWo/w/mtMfIE/IG2K+Ey3ygWanZIBtBW0W2TM=
|
||||||
|
github.com/go-playground/locales v0.13.0 h1:HyWk6mgj5qFqCT5fjGBuRArbVDfE4hi8+e8ceBS/t7Q=
|
||||||
|
github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8=
|
||||||
|
github.com/go-playground/universal-translator v0.16.0/go.mod h1:1AnU7NaIRDWWzGEKwgtJRd2xk99HeFyHw3yid4rvQIY=
|
||||||
|
github.com/go-playground/universal-translator v0.17.0 h1:icxd5fm+REJzpZx7ZfpaD876Lmtgy7VtROAbHHXk8no=
|
||||||
|
github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA=
|
||||||
|
github.com/go-playground/validator/v10 v10.2.0 h1:KgJ0snyC2R9VXYN2rneOtQcw5aHQB1Vv0sFl1UcHBOY=
|
||||||
|
github.com/go-playground/validator/v10 v10.2.0/go.mod h1:uOYAAleCW8F/7oMFd6aG0GOhaH6EGOAJShg8Id5JGkI=
|
||||||
|
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
|
||||||
|
github.com/gofrs/uuid v3.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
|
||||||
|
github.com/gofrs/uuid v3.3.0+incompatible h1:8K4tyRfvU1CYPgJsveYFQMhpFd/wXNM7iK6rR7UHz84=
|
||||||
|
github.com/gofrs/uuid v3.3.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
|
||||||
|
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||||
|
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||||
|
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||||
|
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||||
|
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||||
|
github.com/golang/protobuf v1.3.3 h1:gyjaxf+svBWX08ZjK86iN9geUJF0H6gp2IRKX6Nf6/I=
|
||||||
|
github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
|
||||||
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
|
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
|
||||||
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
|
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
|
||||||
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
|
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
|
||||||
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
|
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
|
||||||
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
|
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
|
||||||
github.com/golang/protobuf v1.4.3 h1:JjCZWpVbqXDqFVmTfYWEVTMIYrL/NPdPSCHPJ0T/raM=
|
github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
|
||||||
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
github.com/golang/protobuf v1.4.2 h1:+Z5KGCizgyZCbGh1KZqA0fcLLkwbsjIzS4aV2v7wJX0=
|
||||||
|
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||||
github.com/gomodule/redigo v2.0.0+incompatible h1:K/R+8tc58AaqLkqG2Ol3Qk+DR/TlNuhuh457pBFPtt0=
|
github.com/gomodule/redigo v2.0.0+incompatible h1:K/R+8tc58AaqLkqG2Ol3Qk+DR/TlNuhuh457pBFPtt0=
|
||||||
github.com/gomodule/redigo v2.0.0+incompatible/go.mod h1:B4C85qUVwatsJoIUNIfCRsp7qO0iAmpGFZ4EELWSbC4=
|
github.com/gomodule/redigo v2.0.0+incompatible/go.mod h1:B4C85qUVwatsJoIUNIfCRsp7qO0iAmpGFZ4EELWSbC4=
|
||||||
|
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
||||||
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||||
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||||
github.com/google/go-cmp v0.4.0 h1:xsAVV57WRhGj6kEIi8ReJzQlHHqcBYCElAvkovg3B/4=
|
|
||||||
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
|
github.com/google/go-cmp v0.5.0 h1:/QaMHBdZ26BB3SSst0Iwl10Epc+xhTquomWX0oZEB6w=
|
||||||
|
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||||
|
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
|
||||||
|
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
|
github.com/gookit/color v1.3.6 h1:Rgbazd4JO5AgSTVGS3o0nvaSdwdrS8bzvIXwtK6OiMk=
|
||||||
|
github.com/gookit/color v1.3.6/go.mod h1:R3ogXq2B9rTbXoSHJ1HyUVAZ3poOJHpd9nQmyGZsfvQ=
|
||||||
github.com/gorilla/context v1.1.1 h1:AWwleXJkX/nhcU9bZSnZoi3h/qGYqQAGhq6zZe/aQW8=
|
github.com/gorilla/context v1.1.1 h1:AWwleXJkX/nhcU9bZSnZoi3h/qGYqQAGhq6zZe/aQW8=
|
||||||
github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg=
|
github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg=
|
||||||
github.com/gorilla/securecookie v1.1.1 h1:miw7JPhV+b/lAHSXz4qd/nN9jRiAFV5FwjeKyCS8BvQ=
|
github.com/gorilla/securecookie v1.1.1 h1:miw7JPhV+b/lAHSXz4qd/nN9jRiAFV5FwjeKyCS8BvQ=
|
||||||
github.com/gorilla/securecookie v1.1.1/go.mod h1:ra0sb63/xPlUeL+yeDciTfxMRAA+MP+HVt/4epWDjd4=
|
github.com/gorilla/securecookie v1.1.1/go.mod h1:ra0sb63/xPlUeL+yeDciTfxMRAA+MP+HVt/4epWDjd4=
|
||||||
github.com/gorilla/sessions v1.1.1 h1:YMDmfaK68mUixINzY/XjscuJ47uXFWSSHzFbBQM0PrE=
|
|
||||||
github.com/gorilla/sessions v1.1.1/go.mod h1:8KCfur6+4Mqcc6S0FEfKuN15Vl5MgXW92AE8ovaJD0w=
|
github.com/gorilla/sessions v1.1.1/go.mod h1:8KCfur6+4Mqcc6S0FEfKuN15Vl5MgXW92AE8ovaJD0w=
|
||||||
github.com/gorilla/sessions v1.2.1 h1:DHd3rPN5lE3Ts3D8rKkQ8x/0kqfeNmBAaiSi+o7FsgI=
|
github.com/gorilla/sessions v1.1.3 h1:uXoZdcdA5XdXF3QzuSlheVRUvjl+1rKY7zBXL68L9RU=
|
||||||
github.com/gorilla/sessions v1.2.1/go.mod h1:dk2InVEVJ0sfLlnXv9EAgkf6ecYs/i80K/zI+bUmuGM=
|
github.com/gorilla/sessions v1.1.3/go.mod h1:8KCfur6+4Mqcc6S0FEfKuN15Vl5MgXW92AE8ovaJD0w=
|
||||||
github.com/json-iterator/go v1.1.10 h1:Kz6Cvnvv2wGdaG/V8yMvfkmNiXq9Ya2KUv4rouJJr68=
|
github.com/jackc/chunkreader v1.0.0 h1:4s39bBR8ByfqH+DKm8rQA3E1LHZWB9XWcrz8fqaZbe0=
|
||||||
github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
github.com/jackc/chunkreader v1.0.0/go.mod h1:RT6O25fNZIuasFJRyZ4R/Y2BbhasbmZXF9QQ7T3kePo=
|
||||||
|
github.com/jackc/chunkreader/v2 v2.0.0/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk=
|
||||||
|
github.com/jackc/chunkreader/v2 v2.0.1 h1:i+RDz65UE+mmpjTfyz0MoVTnzeYxroil2G82ki7MGG8=
|
||||||
|
github.com/jackc/chunkreader/v2 v2.0.1/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk=
|
||||||
|
github.com/jackc/pgconn v0.0.0-20190420214824-7e0022ef6ba3/go.mod h1:jkELnwuX+w9qN5YIfX0fl88Ehu4XC3keFuOJJk9pcnA=
|
||||||
|
github.com/jackc/pgconn v0.0.0-20190824142844-760dd75542eb/go.mod h1:lLjNuW/+OfW9/pnVKPazfWOgNfH2aPem8YQ7ilXGvJE=
|
||||||
|
github.com/jackc/pgconn v0.0.0-20190831204454-2fabfa3c18b7/go.mod h1:ZJKsE/KZfsUgOEh9hBm+xYTstcNHg7UPMVJqRfQxq4s=
|
||||||
|
github.com/jackc/pgconn v1.4.0/go.mod h1:Y2O3ZDF0q4mMacyWV3AstPJpeHXWGEetiFttmq5lahk=
|
||||||
|
github.com/jackc/pgconn v1.5.0/go.mod h1:QeD3lBfpTFe8WUnPZWN5KY/mB8FGMIYRdd8P8Jr0fAI=
|
||||||
|
github.com/jackc/pgconn v1.5.1-0.20200601181101-fa742c524853/go.mod h1:QeD3lBfpTFe8WUnPZWN5KY/mB8FGMIYRdd8P8Jr0fAI=
|
||||||
|
github.com/jackc/pgconn v1.7.0 h1:pwjzcYyfmz/HQOQlENvG1OcDqauTGaqlVahq934F0/U=
|
||||||
|
github.com/jackc/pgconn v1.7.0/go.mod h1:sF/lPpNEMEOp+IYhyQGdAvrG20gWf6A1tKlr0v7JMeA=
|
||||||
|
github.com/jackc/pgio v1.0.0 h1:g12B9UwVnzGhueNavwioyEEpAmqMe1E/BN9ES+8ovkE=
|
||||||
|
github.com/jackc/pgio v1.0.0/go.mod h1:oP+2QK2wFfUWgr+gxjoBH9KGBb31Eio69xUb0w5bYf8=
|
||||||
|
github.com/jackc/pgmock v0.0.0-20190831213851-13a1b77aafa2 h1:JVX6jT/XfzNqIjye4717ITLaNwV9mWbJx0dLCpcRzdA=
|
||||||
|
github.com/jackc/pgmock v0.0.0-20190831213851-13a1b77aafa2/go.mod h1:fGZlG77KXmcq05nJLRkk0+p82V8B8Dw8KN2/V9c/OAE=
|
||||||
|
github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM=
|
||||||
|
github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg=
|
||||||
|
github.com/jackc/pgproto3 v1.1.0 h1:FYYE4yRw+AgI8wXIinMlNjBbp/UitDJwfj5LqqewP1A=
|
||||||
|
github.com/jackc/pgproto3 v1.1.0/go.mod h1:eR5FA3leWg7p9aeAqi37XOTgTIbkABlvcPB3E5rlc78=
|
||||||
|
github.com/jackc/pgproto3/v2 v2.0.0-alpha1.0.20190420180111-c116219b62db/go.mod h1:bhq50y+xrl9n5mRYyCBFKkpRVTLYJVWeCc+mEAI3yXA=
|
||||||
|
github.com/jackc/pgproto3/v2 v2.0.0-alpha1.0.20190609003834-432c2951c711/go.mod h1:uH0AWtUmuShn0bcesswc4aBTWGvw0cAxIJp+6OB//Wg=
|
||||||
|
github.com/jackc/pgproto3/v2 v2.0.0-rc3/go.mod h1:ryONWYqW6dqSg1Lw6vXNMXoBJhpzvWKnT95C46ckYeM=
|
||||||
|
github.com/jackc/pgproto3/v2 v2.0.0-rc3.0.20190831210041-4c03ce451f29/go.mod h1:ryONWYqW6dqSg1Lw6vXNMXoBJhpzvWKnT95C46ckYeM=
|
||||||
|
github.com/jackc/pgproto3/v2 v2.0.1/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA=
|
||||||
|
github.com/jackc/pgproto3/v2 v2.0.5 h1:NUbEWPmCQZbMmYlTjVoNPhc0CfnYyz2bfUAh6A5ZVJM=
|
||||||
|
github.com/jackc/pgproto3/v2 v2.0.5/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA=
|
||||||
|
github.com/jackc/pgservicefile v0.0.0-20200307190119-3430c5407db8/go.mod h1:vsD4gTJCa9TptPL8sPkXrLZ+hDuNrZCnj29CQpr4X1E=
|
||||||
|
github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b h1:C8S2+VttkHFdOOCXJe+YGfa4vHYwlt4Zx+IVXQ97jYg=
|
||||||
|
github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b/go.mod h1:vsD4gTJCa9TptPL8sPkXrLZ+hDuNrZCnj29CQpr4X1E=
|
||||||
|
github.com/jackc/pgtype v0.0.0-20190421001408-4ed0de4755e0/go.mod h1:hdSHsc1V01CGwFsrv11mJRHWJ6aifDLfdV3aVjFF0zg=
|
||||||
|
github.com/jackc/pgtype v0.0.0-20190824184912-ab885b375b90/go.mod h1:KcahbBH1nCMSo2DXpzsoWOAfFkdEtEJpPbVLq8eE+mc=
|
||||||
|
github.com/jackc/pgtype v0.0.0-20190828014616-a8802b16cc59/go.mod h1:MWlu30kVJrUS8lot6TQqcg7mtthZ9T0EoIBFiJcmcyw=
|
||||||
|
github.com/jackc/pgtype v1.2.0/go.mod h1:5m2OfMh1wTK7x+Fk952IDmI4nw3nPrvtQdM0ZT4WpC0=
|
||||||
|
github.com/jackc/pgtype v1.3.1-0.20200510190516-8cd94a14c75a/go.mod h1:vaogEUkALtxZMCH411K+tKzNpwzCKU+AnPzBKZ+I+Po=
|
||||||
|
github.com/jackc/pgtype v1.3.1-0.20200606141011-f6355165a91c/go.mod h1:cvk9Bgu/VzJ9/lxTO5R5sf80p0DiucVtN7ZxvaC4GmQ=
|
||||||
|
github.com/jackc/pgtype v1.5.0 h1:jzBqRk2HFG2CV4AIwgCI2PwTgm6UUoCAK2ofHHRirtc=
|
||||||
|
github.com/jackc/pgtype v1.5.0/go.mod h1:JCULISAZBFGrHaOXIIFiyfzW5VY0GRitRr8NeJsrdig=
|
||||||
|
github.com/jackc/pgx/v4 v4.0.0-20190420224344-cc3461e65d96/go.mod h1:mdxmSJJuR08CZQyj1PVQBHy9XOp5p8/SHH6a0psbY9Y=
|
||||||
|
github.com/jackc/pgx/v4 v4.0.0-20190421002000-1b8f0016e912/go.mod h1:no/Y67Jkk/9WuGR0JG/JseM9irFbnEPbuWV2EELPNuM=
|
||||||
|
github.com/jackc/pgx/v4 v4.0.0-pre1.0.20190824185557-6972a5742186/go.mod h1:X+GQnOEnf1dqHGpw7JmHqHc1NxDoalibchSk9/RWuDc=
|
||||||
|
github.com/jackc/pgx/v4 v4.5.0/go.mod h1:EpAKPLdnTorwmPUUsqrPxy5fphV18j9q3wrfRXgo+kA=
|
||||||
|
github.com/jackc/pgx/v4 v4.6.1-0.20200510190926-94ba730bb1e9/go.mod h1:t3/cdRQl6fOLDxqtlyhe9UWgfIi9R8+8v8GKV5TRA/o=
|
||||||
|
github.com/jackc/pgx/v4 v4.6.1-0.20200606145419-4e5062306904/go.mod h1:ZDaNWkt9sW1JMiNn0kdYBaLelIhw7Pg4qd+Vk6tw7Hg=
|
||||||
|
github.com/jackc/pgx/v4 v4.9.0 h1:6STjDqppM2ROy5p1wNDcsC7zJTjSHeuCsguZmXyzx7c=
|
||||||
|
github.com/jackc/pgx/v4 v4.9.0/go.mod h1:MNGWmViCgqbZck9ujOOBN63gK9XVGILXWCvKLGKmnms=
|
||||||
|
github.com/jackc/puddle v0.0.0-20190413234325-e4ced69a3a2b/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
|
||||||
|
github.com/jackc/puddle v0.0.0-20190608224051-11cab39313c9/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
|
||||||
|
github.com/jackc/puddle v1.1.0/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
|
||||||
|
github.com/jackc/puddle v1.1.1/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
|
||||||
|
github.com/jackc/puddle v1.1.2/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
|
||||||
|
github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
|
||||||
|
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
|
||||||
|
github.com/jinzhu/now v1.1.1 h1:g39TucaRWyV3dwDO++eEc6qf8TVIQ/Da48WmqjZ3i7E=
|
||||||
|
github.com/jinzhu/now v1.1.1/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
|
||||||
|
github.com/json-iterator/go v0.0.0-20180806060727-1624edc4454b/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
|
||||||
|
github.com/json-iterator/go v1.1.5/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
|
||||||
|
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
|
||||||
|
github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||||
|
github.com/json-iterator/go v1.1.9 h1:9yzud/Ht36ygwatGx56VwCZtlI/2AD15T1X2sjSuGns=
|
||||||
|
github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||||
|
github.com/kidstuff/mongostore v0.0.0-20181113001930-e650cd85ee4b/go.mod h1:g2nVr8KZVXJSS97Jo8pJ0jgq29P6H7dG0oplUA86MQw=
|
||||||
|
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||||
|
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||||
|
github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||||
|
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
|
||||||
|
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||||
|
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||||
|
github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw=
|
||||||
|
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
||||||
|
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||||
|
github.com/leodido/go-urn v1.1.0/go.mod h1:+cyI34gQWZcE1eQU7NVgKkkzdXDQHr1dBMtdAPozLkw=
|
||||||
|
github.com/leodido/go-urn v1.2.0 h1:hpXL4XnriNwQ/ABnpepYM/1vCLWNDfUNts8dX3xTG6Y=
|
||||||
|
github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII=
|
||||||
|
github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
||||||
|
github.com/lib/pq v1.1.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
||||||
|
github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
||||||
|
github.com/lib/pq v1.3.0 h1:/qkRGz8zljWiDcFvgpwUpwIAPu3r07TDvs3Rws+o/pU=
|
||||||
|
github.com/lib/pq v1.3.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
||||||
|
github.com/mailru/easyjson v0.0.0-20180823135443-60711f1a8329/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
|
||||||
|
github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
|
||||||
|
github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e h1:hB2xlXdHp/pmPZq0y3QnmWAArdw9PqbmotexnWx/FU8=
|
||||||
|
github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
|
||||||
|
github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ=
|
||||||
|
github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
|
||||||
|
github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
|
||||||
|
github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
|
||||||
|
github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
|
||||||
|
github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
|
||||||
|
github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
|
||||||
|
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
|
||||||
|
github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ=
|
||||||
github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY=
|
github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY=
|
||||||
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
||||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 h1:ZqeYNhU3OHLH3mGKHDcjJRFFRrJa6eAM5H+CtDdOsPc=
|
github.com/memcachier/mc v2.0.1+incompatible/go.mod h1:7bkvFE61leUBvXz+yxsOnGBQSZpBSPIMUQSmmSHvuXc=
|
||||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||||
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742 h1:Esafd1046DLDQ0W1YjYsBW+p8U2u7vzgW2SQVmlNazg=
|
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
|
||||||
|
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||||
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||||
|
github.com/modern-go/reflect2 v0.0.0-20180718012357-94122c33edd3/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||||
|
github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI=
|
||||||
|
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||||
|
github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
|
||||||
|
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
github.com/qiniu/api.v7/v7 v7.8.0 h1:Ye9sHXwCpeDgKJ4BNSoDvXe4yEuU8a/HTT1jKRgkqe8=
|
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||||
github.com/qiniu/api.v7/v7 v7.8.0/go.mod h1:J7pD9UsnxO7XxyRLUHpsWEQd/HgWJNwnn/Za9qEPdEA=
|
github.com/qiniu/api.v7 v7.2.4+incompatible h1:AtooPoOzz93zhThX7XN/H1jALug+Ent/JisGgtNzwAs=
|
||||||
|
github.com/qiniu/api.v7 v7.2.4+incompatible/go.mod h1:V8/EzlTgLN6q0s0CJmg/I81ytsvldSF22F7h6MI02+c=
|
||||||
|
github.com/qiniu/api.v7 v7.2.5+incompatible h1:6KKaGt7MbFzVGSniwzv7qsM/Qv0or4SkRJfmak8LqZE=
|
||||||
|
github.com/qiniu/api.v7/v7 v7.8.2 h1:f08kI0MmsJNzK4sUS8bG3HDH67ktwd/ji23Gkiy2ra4=
|
||||||
|
github.com/qiniu/api.v7/v7 v7.8.2/go.mod h1:FPsIqxh1Ym3X01sANE5ZwXfLZSWoCUp5+jNI8cLo3l0=
|
||||||
|
github.com/qiniu/x v7.0.8+incompatible h1:P4LASsfwJY7SoZ13dwqBwGhZh7HKU8cdFVCUkmz0gZ8=
|
||||||
|
github.com/qiniu/x v7.0.8+incompatible/go.mod h1:KpRKWYG/GaidPQVpoQ2Cvuvtts3gYnoo2PftgdmAiU4=
|
||||||
|
github.com/quasoft/memstore v0.0.0-20180925164028-84a050167438/go.mod h1:wTPjTepVu7uJBYgZ0SdWHQlIas582j6cn2jgk4DDdlg=
|
||||||
|
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
||||||
|
github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ=
|
||||||
|
github.com/rs/zerolog v1.13.0/go.mod h1:YbFCdg8HfsridGWAh22vktObvhZbQsZXe4/zB0OKkWU=
|
||||||
|
github.com/rs/zerolog v1.15.0/go.mod h1:xYTKnLHcpfU2225ny5qZjxnj9NvkumZYjJHlAThCjNc=
|
||||||
|
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||||
|
github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
|
||||||
|
github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24/go.mod h1:M+9NzErvs504Cn4c5DxATwIqPbtswREoFCre64PpcG4=
|
||||||
|
github.com/shopspring/decimal v0.0.0-20200227202807-02e2044944cc h1:jUIKcSPO9MoMJBbEoyE/RJoE8vz7Mb8AjvifMMwSyvY=
|
||||||
|
github.com/shopspring/decimal v0.0.0-20200227202807-02e2044944cc/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o=
|
||||||
|
github.com/shurcooL/sanitized_anchor_name v0.0.0-20170918181015-86672fcb3f95/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
|
||||||
github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo=
|
github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo=
|
||||||
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
|
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
|
||||||
|
github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q=
|
||||||
|
github.com/sirupsen/logrus v1.4.2 h1:SPIRibHv4MatM3XXNO2BJeFLZwZ2LvZgfQ5+UNI2im4=
|
||||||
|
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
|
||||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
|
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
|
github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
|
||||||
|
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||||
github.com/ugorji/go v1.2.2 h1:60ZHIOcsJlo3bJm9CbTVu7OSqT2mxaEmyQbK2NwCkn0=
|
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||||
github.com/ugorji/go v1.2.2/go.mod h1:bitgyERdV7L7Db/Z5gfd5v2NQMNhhiFiZwpgMw2SP7k=
|
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||||
github.com/ugorji/go/codec v1.2.2 h1:08Gah8d+dXj4cZNUHhtuD/S4PXD5WpVbj5B8/ClELAQ=
|
github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=
|
||||||
github.com/ugorji/go/codec v1.2.2/go.mod h1:OM8g7OAy52uYl3Yk+RE/3AS1nXFn1Wh4PPLtupCxbuU=
|
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
|
github.com/swaggo/files v0.0.0-20190704085106-630677cd5c14/go.mod h1:gxQT6pBGRuIGunNf/+tSOB5OHvguWi8Tbt82WOkf35E=
|
||||||
|
github.com/swaggo/gin-swagger v1.2.0/go.mod h1:qlH2+W7zXGZkczuL+r2nEBR2JTT+/lX05Nn6vPhc7OI=
|
||||||
|
github.com/swaggo/gin-swagger v1.3.0 h1:eOmp7r57oUgZPw2dJOjcGNMse9cvXcI4tTqBcnZtPsI=
|
||||||
|
github.com/swaggo/gin-swagger v1.3.0/go.mod h1:oy1BRA6WvgtCp848lhxce7BnWH4C8Bxa0m5SkWx+cS0=
|
||||||
|
github.com/swaggo/swag v1.5.1/go.mod h1:1Bl9F/ZBpVWh22nY0zmYyASPO1lI/zIwRDrpZU+tv8Y=
|
||||||
|
github.com/swaggo/swag v1.6.9 h1:BukKRwZjnEcUxQt7Xgfrt9fpav0hiWw9YimdNO9wssw=
|
||||||
|
github.com/swaggo/swag v1.6.9/go.mod h1:a0IpNeMfGidNOcm2TsqODUh9JHdHu3kxDA0UlGbBKjI=
|
||||||
|
github.com/ugorji/go v1.1.1/go.mod h1:hnLbHMwcvSihnDhEfx2/BzKp2xb0Y+ErdfYcrs9tkJQ=
|
||||||
|
github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc=
|
||||||
|
github.com/ugorji/go v1.1.5-pre/go.mod h1:FwP/aQVg39TXzItUBMwnWp9T9gPQnXw4Poh4/oBQZ/0=
|
||||||
|
github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw=
|
||||||
|
github.com/ugorji/go v1.1.13 h1:nB3O5kBSQGjEQAcfe1aLUYuxmXdFKmYgBZhY32rQb6Q=
|
||||||
|
github.com/ugorji/go v1.1.13/go.mod h1:jxau1n+/wyTGLQoCkjok9r5zFa/FxT6eI5HiHKQszjc=
|
||||||
|
github.com/ugorji/go/codec v0.0.0-20181022190402-e5e69e061d4f/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0=
|
||||||
|
github.com/ugorji/go/codec v1.1.5-pre/go.mod h1:tULtS6Gy1AE1yCENaw4Vb//HLH5njI2tfCQDUqRd8fI=
|
||||||
|
github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY=
|
||||||
|
github.com/ugorji/go/codec v1.1.13 h1:013LbFhocBoIqgHeIHKlV4JWYhqogATYWZhIcH0WHn4=
|
||||||
|
github.com/ugorji/go/codec v1.1.13/go.mod h1:oNVt3Dq+FO91WNQ/9JnHKQP2QJxTzoN7wCBFCq1OeuU=
|
||||||
|
github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA=
|
||||||
|
github.com/urfave/cli/v2 v2.2.0/go.mod h1:SE9GqnLQmjVa0iPEY0f1w3ygNIYcIJ0OKPMoW2caLfQ=
|
||||||
|
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||||
|
github.com/zenazn/goji v0.9.0/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxtB1Q=
|
||||||
|
go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
|
||||||
|
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
|
||||||
|
go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
|
||||||
|
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
|
||||||
|
go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU=
|
||||||
|
go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA=
|
||||||
|
go.uber.org/zap v1.9.1/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
|
||||||
|
go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
|
||||||
|
golang.org/x/crypto v0.0.0-20180820150726-614d502a4dac/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad h1:DN0cp81fZ3njFcrLCytUHRSUkqBjfTo4Tx9RJTWs0EY=
|
golang.org/x/crypto v0.0.0-20190411191339-88737f569e3a/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE=
|
||||||
golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
|
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||||
|
golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||||
|
golang.org/x/crypto v0.0.0-20190911031432-227b76d455e7/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||||
|
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||||
|
golang.org/x/crypto v0.0.0-20200323165209-0ec3e9974c59/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||||
|
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 h1:psW17arqaxU48Z5kZ0CQnkZWQJsqcURM6tKiBApRjXI=
|
||||||
|
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||||
|
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||||
|
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||||
|
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
||||||
|
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||||
|
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||||
|
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
|
||||||
|
golang.org/x/mod v0.3.0 h1:RM4zey1++hCTbCVQfnWeKs9/IEsaBLA8vTkd0WVtmH4=
|
||||||
|
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||||
|
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
|
golang.org/x/net v0.0.0-20180824152047-4bcd98cce591/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
|
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
|
golang.org/x/net v0.0.0-20181005035420-146acd28ed58/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
|
golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
|
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
|
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
golang.org/x/net v0.0.0-20201216054612-986b41b23924 h1:QsnDpLLOKwHBBDa8nDws4DYNc/ryVW2vCpxCs09d4PY=
|
golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
golang.org/x/net v0.0.0-20201216054612-986b41b23924/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
golang.org/x/net v0.0.0-20190611141213-3f473d35a33a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
|
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
|
golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
|
golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
|
golang.org/x/net v0.0.0-20200625001655-4c5254603344 h1:vGXIOMxbNfDTk/aXCmfdLgkrSV+Z2tcbze+pEc3v5W4=
|
||||||
|
golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||||
|
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||||
|
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a h1:DcqTD9SDLc+1P/r1EmRBwnVsrOwW+kk2vWf9n+1sGhs=
|
||||||
|
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sys v0.0.0-20180824143301-4910a1d54f87/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
|
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
|
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
|
golang.org/x/sys v0.0.0-20181228144115-9a3f9b0469bb/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
|
golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20190610200419-93c9922d18ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68 h1:nxC68pudNYkKU6jWhgrqdreuFiOQWj1Fs7T3VrH4Pjw=
|
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd h1:xhmwyvizuTgC2qz7ZlMluP20uW+C3Rm0FD/WLDX8884=
|
||||||
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
|
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
|
||||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
|
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||||
golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k=
|
golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k=
|
||||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
|
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
|
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
|
||||||
|
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||||
|
golang.org/x/tools v0.0.0-20190425163242-31fd60d6bfdc/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||||
|
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||||
|
golang.org/x/tools v0.0.0-20190606050223-4d9ae51c2468/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||||
|
golang.org/x/tools v0.0.0-20190611222205-d73e1c7e250b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||||
|
golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||||
|
golang.org/x/tools v0.0.0-20190823170909-c4a336ef6a2f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
|
golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
|
golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
|
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
|
golang.org/x/tools v0.0.0-20200820010801-b793a1359eac h1:DugppSxw0LSF8lcjaODPJZoDzq0ElTGskTst3ZaBkHI=
|
||||||
|
golang.org/x/tools v0.0.0-20200820010801-b793a1359eac/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
|
||||||
|
golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
|
golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
|
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
|
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
|
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
|
||||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
|
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
||||||
|
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||||
|
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||||
|
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
|
||||||
|
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013 h1:+kGHl1aib/qcwaRi1CbqBZ1rk19r85MNUf8HaBghugY=
|
||||||
|
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
|
||||||
|
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||||
|
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
|
||||||
|
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
|
||||||
|
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
||||||
|
google.golang.org/grpc v1.35.0 h1:TwIQcH3es+MojMVojxxfQ3l3OF2KzlRxML2xZq0kRo8=
|
||||||
|
google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
|
||||||
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
|
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
|
||||||
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
|
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
|
||||||
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
|
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
|
||||||
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
|
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
|
||||||
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
|
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
|
||||||
google.golang.org/protobuf v1.23.0 h1:4MY060fB1DLGMB/7MBTLnwQUY6+F09GEiz6SsrNqyzM=
|
google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||||
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||||
|
google.golang.org/protobuf v1.25.0 h1:Ejskq+SyPohKW+1uil0JJMtmHCgJPJ/qWTxr8qp+R4c=
|
||||||
|
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/go-playground/assert.v1 v1.2.1 h1:xoYuJVE7KT85PYWrN730RguIQO0ePzVRfFMXadIrXTM=
|
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
|
||||||
|
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
||||||
gopkg.in/go-playground/assert.v1 v1.2.1/go.mod h1:9RXL0bg/zibRAgZUYszZSwO/z8Y/a8bDuhia5mkpMnE=
|
gopkg.in/go-playground/assert.v1 v1.2.1/go.mod h1:9RXL0bg/zibRAgZUYszZSwO/z8Y/a8bDuhia5mkpMnE=
|
||||||
gopkg.in/go-playground/validator.v8 v8.18.2 h1:lFB4DoMU6B626w8ny76MV7VX6W2VHct2GVOI3xgiMrQ=
|
|
||||||
gopkg.in/go-playground/validator.v8 v8.18.2/go.mod h1:RX2a/7Ha8BgOhfk7j780h4/u/RRjR0eouCJSH80/M2Y=
|
gopkg.in/go-playground/validator.v8 v8.18.2/go.mod h1:RX2a/7Ha8BgOhfk7j780h4/u/RRjR0eouCJSH80/M2Y=
|
||||||
gopkg.in/mgo.v2 v2.0.0-20190816093944-a6b53ec6cb22 h1:VpOs+IwYnYBaFnrNAeB8UUWtL3vEUnzSCL1nVjPhqrw=
|
gopkg.in/go-playground/validator.v9 v9.29.1/go.mod h1:+c9/zcJMFNgbLvly1L1V+PpxWdVbfP1avr/N00E2vyQ=
|
||||||
gopkg.in/mgo.v2 v2.0.0-20190816093944-a6b53ec6cb22/go.mod h1:yeKp02qBN3iKW1OzL3MGk2IdtZzaj7SFntXj72NppTA=
|
gopkg.in/inconshreveable/log15.v2 v2.0.0-20180818164646-67afb5ed74ec/go.mod h1:aPpfJ7XW+gOuirDoZ8gHhLh3kZ1B08FtV2bbmy7Jv3s=
|
||||||
gopkg.in/yaml.v2 v2.2.1 h1:mUhvW9EsL+naU5Q3cakzfE91YhliOondGd6ZrsDBHQE=
|
gopkg.in/mgo.v2 v2.0.0-20180705113604-9856a29383ce/go.mod h1:yeKp02qBN3iKW1OzL3MGk2IdtZzaj7SFntXj72NppTA=
|
||||||
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
|
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
|
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
|
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
|
gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU=
|
||||||
|
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
|
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
|
||||||
|
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
|
gorm.io/driver/postgres v1.0.5 h1:raX6ezL/ciUmaYTvOq48jq1GE95aMC0CmxQYbxQ4Ufw=
|
||||||
|
gorm.io/driver/postgres v1.0.5/go.mod h1:qrD92UurYzNctBMVCJ8C3VQEjffEuphycXtxOudXNCA=
|
||||||
|
gorm.io/gorm v1.20.4/go.mod h1:0HFTzE/SqkGTzK6TlDPPQbAYCluiVvhzoA1+aVyzenw=
|
||||||
|
gorm.io/gorm v1.20.5 h1:g3tpSF9kggASzReK+Z3dYei1IJODLqNUbOjSuCczY8g=
|
||||||
|
gorm.io/gorm v1.20.5/go.mod h1:0HFTzE/SqkGTzK6TlDPPQbAYCluiVvhzoA1+aVyzenw=
|
||||||
|
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||||
|
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||||
|
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
|
||||||
|
qiniupkg.com/x v7.0.8+incompatible h1:Ek0ZVi5IyaWUAFkJbPRiqlh34xDM4uoKw7KqdpankvU=
|
||||||
|
qiniupkg.com/x v7.0.8+incompatible/go.mod h1:6sLxR5IZ03vMaRAQAY/5MvzofeoBIjO4XE0Njv6V1ms=
|
||||||
|
|||||||