Compare commits

...

13 Commits

Author SHA1 Message Date
Deepzz
b35d7de58a Merge pull request #14 from vyloy/master
fix doSitemap bug
2018-07-06 22:05:12 +08:00
Michael(Zhiyi Weng)
77ea01b7c1 fix doSitemap bug 2018-07-06 10:02:38 +08:00
Razeen
5f608b638d Update README.md 2018-05-17 18:42:00 +08:00
deepzz0
52da8abceb update 2018-05-07 20:51:30 +08:00
deepzz0
f016b28cb6 fix comments duration 2018-05-07 20:45:37 +08:00
henry.chen
01b7643ca5 Merge branch 'master' of github.com:eiblog/eiblog 2018-05-07 16:52:56 +08:00
henry.chen
375d43761b let's encrypt v2 embedded ct,rm about cert's ct 2018-05-07 16:51:54 +08:00
Deepzz
f3e9727947 Set theme jekyll-theme-cayman 2018-05-02 09:45:41 +08:00
Deepzz
911aa963c7 Update eiblog.conf
X-Real_IP -> X-Real-IP
2018-03-27 10:48:16 +08:00
henry.chen
fb66b6871e release v1.4.3 2018-02-09 16:15:34 +08:00
henry.chen
5ae76f243e fixed #6,发布文章异步提交,随机 session key等 2018-02-09 13:50:34 +08:00
deepzz0
051b034e51 1. 修复编辑专题:按钮显示"新增专题"错误 2. 编辑专题链接移动到专题名称 2018-02-04 12:39:35 +08:00
Deepzz
27439ecc71 Update install.md 2018-02-01 21:24:00 +08:00
23 changed files with 143 additions and 88 deletions

View File

@@ -1,5 +1,20 @@
# Eiblog Changelog # Eiblog Changelog
## v1.4.4 (2018-05-07)
* 修复基础评论分钟数计算错误
* let's encrypt v2证书内嵌ct故移除有关ct内容
## v1.4.3 (2018-02-09)
* 修复博客初始化后about 页面不能够评论 #6
* 修复编辑专题,按钮显示“添加专题”错误
* 优化“添加文章”从同步改为异步推送feedesdisqus。速度显著提升
* **重要*)头像图片从 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) ## v1.4.1 (2018-01-14)
* 修复创建新文章disqus 不收录bug * 修复创建新文章disqus 不收录bug
* 修复创建新文章归档页面不刷新bug * 修复创建新文章归档页面不刷新bug

View File

@@ -26,7 +26,7 @@ dist:
gencert:makedir gencert:makedir
@if [ ! -n "$(sans)" ]; then \ @if [ ! -n "$(sans)" ]; then \
printf "Need one argument [sans=params]\n"; \ printf "Need one argument [sans=params]\n"; \
printf "example: sans=\"-d domain -d domain\"\n"; \ printf "example: sans=\"-d domain -d *.domain\"\n"; \
exit 1; \ exit 1; \
fi; \ fi; \
if [ ! -n "$(cn)" ]; then \ if [ ! -n "$(cn)" ]; then \
@@ -39,22 +39,18 @@ gencert:makedir
fi fi
@echo "generate rsa cert..." @echo "generate rsa cert..."
@$(acme.sh) --force --issue --dns dns_ali $(sans) --log \ @$(acme.sh) --force --issue --dns dns_ali $(sans) \
--renew-hook "ct-submit ctlog-gen2.api.venafi.com < $(config)/ssl/domain.rsa.pem > $(config)/scts/rsa/venafi.sct \ --renew-hook "$(acme.sh) --install-cert -d $(cn) \
&& ct-submit ctlog.wosign.com < $(config)/ssl/domain.rsa.pem > $(config)/scts/rsa/wosign.sct" --key-file $(config)/ssl/domain.rsa.key \
@$(acme.sh) --install-cert -d $(cn) \ --fullchain-file $(config)/ssl/domain.rsa.pem \
--key-file $(config)/ssl/domain.rsa.key \ --reloadcmd \"service nginx force-reload\""
--fullchain-file $(config)/ssl/domain.rsa.pem \
--reloadcmd "service nginx force-reload"
@echo "generate ecc cert..." @echo "generate ecc cert..."
@$(acme.sh) --force --issue --dns dns_ali $(sans) -k ec-256 --log \ @$(acme.sh) --force --issue --dns dns_ali $(sans) -k ec-256 \
--renew-hook "ct-submit ctlog-gen2.api.venafi.com < $(config)/ssl/domain.ecc.pem > $(config)/scts/ecc/venafi.sct \ --renew-hook "$(acme.sh) --install-cert -d $(cn) --ecc \
&& ct-submit ctlog.wosign.com < $(config)/ssl/domain.ecc.pem > $(config)/scts/ecc/wosign.sct" --key-file $(config)/ssl/domain.ecc.key \
@$(acme.sh) --install-cert -d $(cn) --ecc \ --fullchain-file $(config)/ssl/domain.ecc.pem \
--key-file $(config)/ssl/domain.ecc.key \ --reloadcmd \"service nginx force-reload\""
--fullchain-file $(config)/ssl/domain.ecc.pem \
--reloadcmd "service nginx force-reload"
dhparams: dhparams:
@openssl dhparam -out $(config)/ssl/dhparams.pem 2048 @openssl dhparam -out $(config)/ssl/dhparams.pem 2048
@@ -63,7 +59,7 @@ ssticket:
@openssl rand 48 > $(config)/ssl/session_ticket.key @openssl rand 48 > $(config)/ssl/session_ticket.key
makedir: makedir:
@mkdir -p $(config)/ssl $(config)/scts/rsa $(config)/scts/ecc @mkdir -p $(config)/ssl
clean: clean:

View File

@@ -86,6 +86,6 @@
### 成功搭建者博客 ### 成功搭建者博客
* [https://razeen.me](https://razeen.me) - Razeen's Blog * [https://blog.netcj.com](https://blog.netcj.com) - Razeen's Blog
如果你的博客使用`Eiblog`搭建,你可以在 [这里](https://github.com/eiblog/eiblog/issues/1) 提交网址。 如果你的博客使用`Eiblog`搭建,你可以在 [这里](https://github.com/eiblog/eiblog/issues/1) 提交网址。

34
api.go
View File

@@ -231,12 +231,15 @@ func apiPostAdd(c *gin.Context) {
} }
cid = int(artc.ID) cid = int(artc.ID)
if !artc.IsDraft { if !artc.IsDraft {
// elastic // 异步执行,快
ElasticIndex(artc) go func() {
// rss // elastic
DoPings(slug) ElasticIndex(artc)
// disqus // rss
ThreadCreate(artc) DoPings(slug)
// disqus
ThreadCreate(artc)
}()
} }
return return
} }
@@ -260,14 +263,17 @@ func apiPostAdd(c *gin.Context) {
} }
if !artc.IsDraft { if !artc.IsDraft {
ReplaceArticle(a, artc) ReplaceArticle(a, artc)
// elastic // 异步执行,快
ElasticIndex(artc) go func() {
// rss // elastic
DoPings(slug) ElasticIndex(artc)
// disqus // rss
if a == nil { DoPings(slug)
ThreadCreate(artc) // disqus
} if a == nil {
ThreadCreate(artc)
}
}()
} }
} }

View File

@@ -9,9 +9,11 @@ server {
# ip 黑名单 # ip 黑名单
include /data/eiblog/conf/nginx/ip.blacklist; include /data/eiblog/conf/nginx/ip.blacklist;
# 现在一般证书是内置的。letsencrypt 暂未 # letsencrypt v2已内置
# https://imququ.com/post/certificate-transparency.html#toc-2 # https://imququ.com/post/certificate-transparency.html#toc-2
ssl_ct on; #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 # https://imququ.com/post/why-can-not-turn-on-ocsp-stapling.html
@@ -20,10 +22,8 @@ server {
# 站点证书 + 中间证书,私钥 # 站点证书 + 中间证书,私钥
ssl_certificate /data/eiblog/conf/ssl/domain.rsa.pem; ssl_certificate /data/eiblog/conf/ssl/domain.rsa.pem;
ssl_certificate_key /data/eiblog/conf/ssl/domain.rsa.key; ssl_certificate_key /data/eiblog/conf/ssl/domain.rsa.key;
ssl_ct_static_scts /data/eiblog/conf/scts/rsa/;
# ssl_certificate /data/eiblog/conf/ssl/domain.ecc.pem; # ssl_certificate /data/eiblog/conf/ssl/domain.ecc.pem;
# ssl_certificate_key /data/eiblog/conf/ssl/domain.ecc.key; # ssl_certificate_key /data/eiblog/conf/ssl/domain.ecc.key;
# ssl_ct_static_scts /data/eiblog/conf/scts/ecc/;
# openssl dhparam -out dhparams.pem 2048 # openssl dhparam -out dhparams.pem 2048
# https://weakdh.org/sysadmin.html # https://weakdh.org/sysadmin.html
@@ -105,7 +105,7 @@ server {
proxy_set_header Connection ""; proxy_set_header Connection "";
proxy_set_header Host deepzz.com; proxy_set_header Host deepzz.com;
proxy_set_header X-Real_IP $remote_addr; proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_pass http://127.0.0.1:9000; proxy_pass http://127.0.0.1:9000;

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

50
db.go
View File

@@ -75,9 +75,9 @@ func init() {
logd.Fatal(err) logd.Fatal(err)
} }
// 读取帐号信息 // 读取帐号信息
Ei = loadAccount() loadAccount()
// 获取文章数据 // 获取文章数据
Ei.Articles = loadArticles() loadArticles()
// 生成markdown文档 // 生成markdown文档
go generateMarkdown() go generateMarkdown()
// 启动定时器 // 启动定时器
@@ -87,12 +87,13 @@ func init() {
} }
// 读取或初始化帐号信息 // 读取或初始化帐号信息
func loadAccount() (a *Account) { func loadAccount() {
a = &Account{} Ei = &Account{}
err := mgo.FindOne(DB, COLLECTION_ACCOUNT, mgo.M{"username": setting.Conf.Account.Username}, a) err := mgo.FindOne(DB, COLLECTION_ACCOUNT, mgo.M{"username": setting.Conf.Account.Username}, Ei)
// 初始化用户数据 // 初始化用户数据
if err == mgo.ErrNotFound { if err == mgo.ErrNotFound {
a = &Account{ logd.Printf("Initializing account: %s\n", setting.Conf.Account.Username)
Ei = &Account{
Username: setting.Conf.Account.Username, Username: setting.Conf.Account.Username,
Password: EncryptPasswd(setting.Conf.Account.Username, setting.Conf.Account.Password), Password: EncryptPasswd(setting.Conf.Account.Username, setting.Conf.Account.Password),
Email: setting.Conf.Account.Email, Email: setting.Conf.Account.Email,
@@ -100,29 +101,28 @@ func loadAccount() (a *Account) {
Address: setting.Conf.Account.Address, Address: setting.Conf.Account.Address,
CreateTime: time.Now(), CreateTime: time.Now(),
} }
a.BlogName = setting.Conf.Blogger.BlogName Ei.BlogName = setting.Conf.Blogger.BlogName
a.SubTitle = setting.Conf.Blogger.SubTitle Ei.SubTitle = setting.Conf.Blogger.SubTitle
a.BeiAn = setting.Conf.Blogger.BeiAn Ei.BeiAn = setting.Conf.Blogger.BeiAn
a.BTitle = setting.Conf.Blogger.BTitle Ei.BTitle = setting.Conf.Blogger.BTitle
a.Copyright = setting.Conf.Blogger.Copyright Ei.Copyright = setting.Conf.Blogger.Copyright
err = mgo.Insert(DB, COLLECTION_ACCOUNT, a) err = mgo.Insert(DB, COLLECTION_ACCOUNT, Ei)
generateTopic() generateTopic()
} else if err != nil { } else if err != nil {
logd.Fatal(err) logd.Fatal(err)
} }
a.CH = make(chan string, 2) Ei.CH = make(chan string, 2)
a.MapArticles = make(map[string]*Article) Ei.MapArticles = make(map[string]*Article)
a.Tags = make(map[string]SortArticles) Ei.Tags = make(map[string]SortArticles)
return
} }
func loadArticles() (artcs SortArticles) { func loadArticles() {
err := mgo.FindAll(DB, COLLECTION_ARTICLE, mgo.M{"isdraft": false, "deletetime": mgo.M{"$eq": time.Time{}}}, &artcs) err := mgo.FindAll(DB, COLLECTION_ARTICLE, mgo.M{"isdraft": false, "deletetime": mgo.M{"$eq": time.Time{}}}, &Ei.Articles)
if err != nil { if err != nil {
logd.Fatal(err) logd.Fatal(err)
} }
sort.Sort(artcs) sort.Sort(Ei.Articles)
for i, v := range artcs { for i, v := range Ei.Articles {
// 渲染文章 // 渲染文章
GenerateExcerptAndRender(v) GenerateExcerptAndRender(v)
Ei.MapArticles[v.Slug] = v Ei.MapArticles[v.Slug] = v
@@ -131,16 +131,15 @@ func loadArticles() (artcs SortArticles) {
continue continue
} }
if i > 0 { if i > 0 {
v.Prev = artcs[i-1] v.Prev = Ei.Articles[i-1]
} }
if artcs[i+1].ID >= setting.Conf.General.StartID { if Ei.Articles[i+1].ID >= setting.Conf.General.StartID {
v.Next = artcs[i+1] v.Next = Ei.Articles[i+1]
} }
upArticle(v, false) upArticle(v, false)
} }
Ei.CH <- SERIES_MD Ei.CH <- SERIES_MD
Ei.CH <- ARCHIVE_MD Ei.CH <- ARCHIVE_MD
return
} }
// generate series,archive markdown // generate series,archive markdown
@@ -209,6 +208,9 @@ func generateTopic() {
CreateTime: time.Time{}, CreateTime: time.Time{},
UpdateTime: time.Time{}, UpdateTime: time.Time{},
} }
// 推送到 disqus
go func() { ThreadCreate(about) }()
blogroll := &Article{ blogroll := &Article{
ID: mgo.NextVal(DB, COUNTER_ARTICLE), ID: mgo.NextVal(DB, COUNTER_ARTICLE),
Author: setting.Conf.Account.Username, Author: setting.Conf.Account.Username,

View File

@@ -6,8 +6,6 @@ services:
volumes: volumes:
- /data/eiblog/mgodb:/data/db - /data/eiblog/mgodb:/data/db
restart: always restart: always
ports:
- 27017:27017
elasticsearch: elasticsearch:
image: elasticsearch:2.4.1 image: elasticsearch:2.4.1
container_name: eisearch container_name: eisearch

1
docs/_config.yml Normal file
View File

@@ -0,0 +1 @@
theme: jekyll-theme-cayman

View File

@@ -90,7 +90,7 @@ $ docker run -d --name eisearch \
| ------------------ | ---------------------------------------- | ---------------------------------------- | | ------------------ | ---------------------------------------- | ---------------------------------------- |
| favicon.ico | st.example.com/static/img/favicon.ico | cdn 中的文件名为 `static/img/favicon.ico`。你也可以复制 favicon.ico 到 static 文件夹下,通过 example.com/favicon.ico 也是能够访问到。docker 用户可能需要重新打包镜像。 | | favicon.ico | st.example.com/static/img/favicon.ico | cdn 中的文件名为 `static/img/favicon.ico`。你也可以复制 favicon.ico 到 static 文件夹下,通过 example.com/favicon.ico 也是能够访问到。docker 用户可能需要重新打包镜像。 |
| bg04.jpg | st.example.com/static/img/bg04.jpg | 首页左侧的大背景图,需要更名请到 views/st_blog.css 修改。 | | bg04.jpg | st.example.com/static/img/bg04.jpg | 首页左侧的大背景图,需要更名请到 views/st_blog.css 修改。 |
| avatar.jpg | st.example.com/static/img/avatar.jpg | 头像 | | avatar.png | st.example.com/static/img/avatar.png | 头像 |
| blank.gif | st.example.com/static/img/blank.gif | 空白图片,[下载](https://st.deepzz.com/static/img/blank.gif) | | blank.gif | st.example.com/static/img/blank.gif | 空白图片,[下载](https://st.deepzz.com/static/img/blank.gif) |
| default_avatar.png | st.example.com/static/img/default_avatar.png | disqus 默认图片,[下载](https://st.deepzz.com/static/img/default_avatar.png) | | default_avatar.png | st.example.com/static/img/default_avatar.png | disqus 默认图片,[下载](https://st.deepzz.com/static/img/default_avatar.png) |
| disqus.js | st.example.com/static/js/disqus_xxx.js | disqus 文件,你可以通过 https://short_name.disqus.com/embed.js 下载你的专属文件,并上传到七牛。更新配置文件 app.yml。 | | disqus.js | st.example.com/static/js/disqus_xxx.js | disqus 文件,你可以通过 https://short_name.disqus.com/embed.js 下载你的专属文件,并上传到七牛。更新配置文件 app.yml。 |

View File

@@ -6,6 +6,7 @@ import (
"errors" "errors"
"fmt" "fmt"
"io/ioutil" "io/ioutil"
"net"
"net/http" "net/http"
"regexp" "regexp"
"strings" "strings"
@@ -23,10 +24,20 @@ const (
ES_DATE = `{"range":{"date":{"gte":"%s","lte": "%s","format": "yyyy-MM-dd||yyyy-MM||yyyy"}}}` // 2016-10||/M ES_DATE = `{"range":{"date":{"gte":"%s","lte": "%s","format": "yyyy-MM-dd||yyyy-MM||yyyy"}}}` // 2016-10||/M
) )
var es *ElasticService var (
ErrUninitializedES = errors.New("uninitialized elasticsearch")
es *ElasticService
)
// 初始化 Elasticsearch 服务器 // 初始化 Elasticsearch 服务器
func init() { func init() {
_, err := net.LookupIP("elasticsearch")
if err != nil {
logd.Info(err)
return
}
es = &ElasticService{url: "http://elasticsearch:9200", c: new(http.Client)} es = &ElasticService{url: "http://elasticsearch:9200", c: new(http.Client)}
initIndex() initIndex()
} }
@@ -41,7 +52,11 @@ func initIndex() {
} }
// 查询 // 查询
func Elasticsearch(qStr string, size, from int) *ESSearchResult { func Elasticsearch(qStr string, size, from int) (*ESSearchResult, error) {
if es == nil {
return nil, ErrUninitializedES
}
// 分析查询字符串 // 分析查询字符串
reg := regexp.MustCompile(`(tag|slug|date):`) reg := regexp.MustCompile(`(tag|slug|date):`)
indexs := reg.FindAllStringIndex(qStr, -1) indexs := reg.FindAllStringIndex(qStr, -1)
@@ -92,14 +107,17 @@ func Elasticsearch(qStr string, size, from int) *ESSearchResult {
} }
docs, err := IndexQueryDSL(INDEX, TYPE, size, from, []byte(dsl)) docs, err := IndexQueryDSL(INDEX, TYPE, size, from, []byte(dsl))
if err != nil { if err != nil {
logd.Error(err) return nil, err
return nil
} }
return docs return docs, nil
} }
// 添加或更新索引 // 添加或更新索引
func ElasticIndex(artc *Article) error { func ElasticIndex(artc *Article) error {
if es == nil {
return ErrUninitializedES
}
img := PickFirstImage(artc.Content) img := PickFirstImage(artc.Content)
mapping := map[string]interface{}{ mapping := map[string]interface{}{
"title": artc.Title, "title": artc.Title,
@@ -115,6 +133,10 @@ func ElasticIndex(artc *Article) error {
// 删除索引 // 删除索引
func ElasticDelIndex(ids []int32) error { func ElasticDelIndex(ids []int32) error {
if es == nil {
return ErrUninitializedES
}
var target []string var target []string
for _, id := range ids { for _, id := range ids {
target = append(target, fmt.Sprint(id)) target = append(target, fmt.Sprint(id))

View File

@@ -199,10 +199,12 @@ func HandleSearchPage(c *gin.Context) {
start = 1 start = 1
} }
h["Word"] = q h["Word"] = q
var result *ESSearchResult
vals := c.Request.URL.Query() vals := c.Request.URL.Query()
result = Elasticsearch(q, setting.Conf.General.PageNum, start-1) result, err := Elasticsearch(q, setting.Conf.General.PageNum, start-1)
if result != nil { if err != nil {
logd.Error(err)
} else {
result.Took /= 1000 result.Took /= 1000
for i, v := range result.Hits.Hits { for i, v := range result.Hits.Hits {
if artc := Ei.MapArticles[result.Hits.Hits[i].Source.Slug]; len(v.Highlight.Content) == 0 && artc != nil { if artc := Ei.MapArticles[result.Hits.Hits[i].Source.Slug]; len(v.Highlight.Content) == 0 && artc != nil {

View File

@@ -125,7 +125,7 @@ func ConvertStr(str string) string {
} else if h := d*24 + h2 - h1; h > 1 || (h == 1 && mi2-mi1 >= 0) { } else if h := d*24 + h2 - h1; h > 1 || (h == 1 && mi2-mi1 >= 0) {
return fmt.Sprintf(HOURS_AGO, h) return fmt.Sprintf(HOURS_AGO, h)
} else if mi := h*60 + mi2 - mi1; mi > 1 || (mi == 1 && s2-s1 >= 0) { } else if mi := h*60 + mi2 - mi1; mi > 1 || (mi == 1 && s2-s1 >= 0) {
return fmt.Sprintf(MINUTES_AGO, m) return fmt.Sprintf(MINUTES_AGO, mi)
} }
return JUST_NOW return JUST_NOW
} }

View File

@@ -44,15 +44,21 @@ func TestPickFirstImage(t *testing.T) {
} }
func TestCovertStr(t *testing.T) { func TestCovertStr(t *testing.T) {
now := time.Now().UTC()
testStr := []string{ testStr := []string{
time.Now().Format("2006-01-02T15:04:05"), now.Format("2006-01-02T15:04:05"),
now.Add(-time.Second * 20).Format("2006-01-02T15:04:05"),
now.Add(-time.Minute).Format("2006-01-02T15:04:05"),
now.Add(-time.Minute * 2).Format("2006-01-02T15:04:05"),
now.Add(-time.Minute * 20).Format("2006-01-02T15:04:05"),
now.Add(-time.Hour).Format("2006-01-02T15:04:05"),
now.Add(-time.Hour * 2).Format("2006-01-02T15:04:05"),
now.Add(-time.Hour * 24).Format("2006-01-02T15:04:05"),
} }
expectStr := []string{ time.Sleep(time.Second)
JUST_NOW, t.Log(now.Format("2006-01-02T15:04:05"))
} for _, v := range testStr {
t.Log(v, ConvertStr(v))
for i, v := range testStr {
assert.Equal(t, expectStr[i], ConvertStr(v))
} }
} }

View File

@@ -2,6 +2,7 @@
package main package main
import ( import (
"crypto/rand"
"fmt" "fmt"
"text/template" "text/template"
"time" "time"
@@ -27,7 +28,12 @@ func init() {
} }
router = gin.Default() router = gin.Default()
store := sessions.NewCookieStore([]byte("eiblog321")) b := make([]byte, 16)
_, err := rand.Read(b)
if err != nil {
logd.Fatal(err)
}
store := sessions.NewCookieStore(b)
store.Options(sessions.Options{ store.Options(sessions.Options{
MaxAge: 86400 * 7, MaxAge: 86400 * 7,
Path: "/", Path: "/",
@@ -43,7 +49,7 @@ func init() {
} }
return false return false
}) })
_, err := Tmpl.ParseFiles(files...) _, err = Tmpl.ParseFiles(files...)
if err != nil { if err != nil {
logd.Fatal(err) logd.Fatal(err)
} }

View File

@@ -60,7 +60,7 @@
</ul> </ul>
</nav> </nav>
<div class="operate"> <div class="operate">
<a target="_self" title="{{.LastLogin}}" href="/admin/profile" class="author">{{.Author}}</a><a class="exit" href="/admin/login?logout=true">登出</a><a target="_back" href="/">网站</a> <a target="_self" title="{{.LastLogin}}" href="/admin/profile" class="author">{{.Author}}</a><a class="exit" href="/admin/login?logout=true">登出</a><a target="_blank" href="/">网站</a>
</div> </div>
</div> </div>
<div class="main"> <div class="main">

View File

@@ -64,7 +64,7 @@
<td><a href="/post/{{.Slug}}.html#comments" class="balloon-button size-1">{{.Count}}</a></td> <td><a href="/post/{{.Slug}}.html#comments" class="balloon-button size-1">{{.Count}}</a></td>
<td> <td>
<a href="/admin/write-post?cid={{.ID}}">{{.Title}}</a> <a href="/admin/write-post?cid={{.ID}}">{{.Title}}</a>
<a target="_black" href="/post/{{.Slug}}.html" title="浏览 {{.Title}}"><i class="i-exlink"></i></a> <a target="_blank" href="/post/{{.Slug}}.html" title="浏览 {{.Title}}"><i class="i-exlink"></i></a>
</td> </td>
<td>{{.Author}}</td> <td>{{.Author}}</td>
<td>{{if gt .SerieID 0}}专题ID:{{.SerieID}}{{else}}--{{end}}</td> <td>{{if gt .SerieID 0}}专题ID:{{.SerieID}}{{else}}--{{end}}</td>

View File

@@ -39,7 +39,7 @@
<ul class="typecho-option typecho-option-submit" id="typecho-option-item--6"> <ul class="typecho-option typecho-option-submit" id="typecho-option-item--6">
<li> <li>
<button type="submit" class="btn primary"> <button type="submit" class="btn primary">
增加专题</button> {{if .Edit}}更新专题{{else}}新增专题{{end}}</button>
</li> </li>
</ul> </ul>
</form> </form>

View File

@@ -45,10 +45,11 @@
<td> <td>
<input type="checkbox" value="{{.ID}}" name="mid[]" /> <input type="checkbox" value="{{.ID}}" name="mid[]" />
</td> </td>
<td><a href="/admin/add-serie?mid={{.ID}}">{{.ID}}</a> <td>{{.ID}}</td>
<a href="/series.html#toc-{{.ID}}" title="浏览 {{.Name}}"><i class="i-exlink"></i></a> <td>
<a href="/admin/add-serie?mid={{.ID}}">{{.Name}}</a>
<a target="_blank" href="/series.html#toc-{{.ID}}" title="浏览 {{.Name}}"><i class="i-exlink"></i></a>
</td> </td>
<td>{{.Name}}</td>
<td>{{dateformat .CreateTime "2006/01/02 15:04"}}</td> <td>{{dateformat .CreateTime "2006/01/02 15:04"}}</td>
<td><a class="balloon-button left size-50" href="#">{{len .Articles}}</a></td> <td><a class="balloon-button left size-50" href="#">{{len .Articles}}</a></td>
</tr> </tr>

2
xml.go
View File

@@ -83,7 +83,7 @@ func doSitemap() {
logd.Error(err) logd.Error(err)
return return
} }
time.AfterFunc(time.Hour*24, doFeed) time.AfterFunc(time.Hour*24, doSitemap)
} }
// 渲染 opensearch // 渲染 opensearch