Compare commits

...

373 Commits

Author SHA1 Message Date
henry.chen
b75f7d6a96 chore(release): 3.0.10 2025-11-12 09:43:51 +08:00
henry.chen
5aa8152802 fix: default RUN_MODE=prod 2025-11-12 09:43:48 +08:00
henry.chen
1a9628a5dc chore(release): 3.0.9 2025-11-12 09:34:03 +08:00
henry.chen
71c02266bc fix: image RUN_MODE 2025-11-12 09:33:49 +08:00
henry.chen
4b71e288f3 chore(release): 3.0.8 2025-10-14 10:03:04 +08:00
henry.chen
523ee64931 fix: upload file path 2025-10-14 10:02:36 +08:00
henry.chen
422aacdc09 chore: update 2025-08-05 14:33:28 +08:00
henry.chen
07fc49db5c chore: update docs 2025-08-05 14:26:17 +08:00
henry.chen
666161d37e chore(release): 3.0.7 2025-07-26 09:52:30 +08:00
henry.chen
11b22da339 fix: feed not generate 2025-07-26 09:52:18 +08:00
henry.chen
bd9a45078b chore(release): 3.0.6 2025-07-25 18:05:33 +08:00
henry.chen
629ad782c4 fix: feed & sitemap not generate 2025-07-25 18:05:23 +08:00
henry.chen
5e63f5e69d chore(release): 3.0.5 2025-07-25 17:55:44 +08:00
henry.chen
33afbd351d fix: mongodb uri error 2025-07-25 17:55:14 +08:00
henry.chen
eaeeaaafb9 fix: RUN_MODE error 2025-07-25 17:21:02 +08:00
henry.chen
ae3fb35435 chore: update 2025-07-25 13:37:49 +08:00
henry.chen
c1d73f1a45 fix: admin login session 2025-07-25 13:35:06 +08:00
henry.chen
fd7981b4f9 chore(release): 3.0.4 2025-07-25 10:59:04 +08:00
henry.chen
9f77fb7b3f fix(ci): golang version 2025-07-25 10:59:02 +08:00
henry.chen
f201499945 chore(release): 3.0.3 2025-07-25 10:10:26 +08:00
henry.chen
d7736abb25 chore: bump go version: v1.23.0 2025-07-25 10:07:43 +08:00
henry.chen
3c4fa6d08a chore(release): 3.0.2 2025-07-25 09:35:43 +08:00
henry.chen
ccb5e4546e fix: ci 2025-07-25 09:35:40 +08:00
henry.chen
6ce6411da0 chore(release): 3.0.1 2025-07-25 09:29:28 +08:00
henry.chen
cb2ed7cb82 fix: dist tar 2025-07-25 09:27:16 +08:00
henry.chen
ea566d1650 chore: update ci 2025-07-24 18:28:41 +08:00
henry.chen
88dccca295 chore(release): 3.0.0 2025-07-24 17:57:09 +08:00
henry.chen
cb11a2b8db chore: replace twitter icon 2025-07-22 11:38:37 +08:00
henry.chen
ed03a264f0 chore: update 2025-07-21 20:10:16 +08:00
henry.chen
aee4194c71 fix: custom page support embed 2025-07-21 20:07:26 +08:00
henry.chen
b69248f6a4 feat: support custom page 2025-07-17 15:55:32 +08:00
henry.chen
24bfe528b2 fix: ci dist tar 2025-07-17 15:37:30 +08:00
henry.chen
23e35dcefa chore: update compose.yml 2025-07-17 15:35:08 +08:00
henry.chen
f4c70b46c1 fix: ci 2025-07-17 15:30:30 +08:00
henry.chen
e5100fa018 feat: twofactor bind 2025-07-17 15:24:46 +08:00
henry.chen
91e1731909 chore: optmize code 2025-07-17 14:01:17 +08:00
henry.chen
4abe528742 chore: mv asset 2025-07-17 10:57:44 +08:00
henry.chen
be0280ac56 chore: update backup 2025-07-17 10:52:43 +08:00
henry.chen
a0b41d08bd chore: update 2025-07-16 19:57:39 +08:00
henry.chen
8fcabd5e15 refactor: refactor eiblog 2025-07-16 19:45:50 +08:00
henry.chen
0a410f09f3 chore(release): 2.2.17 2025-04-22 15:18:23 +08:00
henry.chen
0fe849ae67 fix: backup file auto delete 2025-04-22 15:12:50 +08:00
henry.chen
c06a32a268 chore(release): 2.2.16 2025-03-13 13:40:50 +08:00
henry.chen
79fccb958c fix: empty "beian" display issue 2025-03-13 13:40:33 +08:00
henry.chen
8e2679e49f chore(release): 2.2.15 2025-01-01 02:53:10 +08:00
henry.chen
52fe7303f3 fix: disqus list posts 2025-01-01 02:49:21 +08:00
henry.chen
616248d33f fix: disqus thread not store 2025-01-01 02:19:48 +08:00
henry.chen
6e1965a764 chore: replace ioutil -> io 2024-11-04 10:31:28 +08:00
henry.chen
27bc610a31 chore(release): 2.2.14 2024-10-10 13:39:32 +08:00
henry.chen
b53fc91ce7 fix: 1. bei_an cannot update error
2. CleanArticles deleted all article error in rdbms, fixed #43,fixed #44
2024-10-10 13:38:38 +08:00
henry.chen
720387ecd5 chore: update 2024-05-28 13:09:10 +08:00
henry.chen
88f23bd1a0 fix(serie): update serie did not rerender 2024-01-23 09:43:17 +08:00
henry.chen
6a2d720d36 chore(release): 2.2.13 2024-01-02 21:40:39 +08:00
henry.chen
95e55ee13c fix: load more comments 2024-01-02 21:39:36 +08:00
henry.chen
ca293a4933 chore(release): 2.2.12 2024-01-02 20:01:16 +08:00
henry.chen
c82d73ca34 chore: downgrade mongodb driver 2024-01-02 20:01:14 +08:00
henry.chen
433064de00 chore(release): 2.2.11 2024-01-02 19:09:40 +08:00
henry.chen
9d71ca8198 fix(disqus): fix returned posts list not have parent post 2024-01-02 19:09:37 +08:00
henry.chen
7c938bf024 chore(release): 2.2.10 2023-12-22 14:12:15 +08:00
henry.chen
65fcc69efa chore: bump all gorm & driver version 2023-12-22 14:10:36 +08:00
henry.chen
d06bab72a5 chore: bump gin swagger version to v1.6.0 2023-12-22 14:03:57 +08:00
henry.chen
95900eec1a chore: rm http header: Expect-CT 2023-12-07 09:55:30 +08:00
henry.chen
dfae78891d chore: update ad config 2023-12-01 13:46:29 +08:00
henry.chen
c515e33e2c chore(release): 2.2.9 2023-09-25 18:14:51 +08:00
henry.chen
5b12dd625b chore: update config 2023-09-25 18:14:41 +08:00
henry.chen
9b918caccd fix: google analytics not work, supported ga4 measurement protocol 2023-09-25 18:12:57 +08:00
Deepzz
2be53379e1 Update writing.md 2023-09-13 10:49:51 +08:00
Deepzz
944e27c58a Update app.yml 2023-09-13 10:45:56 +08:00
Deepzz
92baf970bc Update docker-compose.yml 2023-07-12 17:33:18 +08:00
henry.chen
64a754167a chore(release): 2.2.8 2023-07-12 17:30:37 +08:00
henry.chen
af2a20c34a chore: update 2023-07-12 17:30:34 +08:00
henry.chen
f28d0e77e0 fix(backup): restore db and tests 2023-07-12 17:26:44 +08:00
henry.chen
9a1b4db61a chore: update 2023-07-12 15:14:47 +08:00
henry.chen
c9d04aded3 chore(release): 2.2.7 2023-07-12 15:01:40 +08:00
henry.chen
ee51f678cb fix(backup): 数据恢复错误 2023-07-12 14:59:11 +08:00
Deepzz
434c1bf168 Update README.md 2023-06-19 23:19:00 +08:00
henry.chen
d2de603957 chore(release): 2.2.6 2023-06-19 23:13:56 +08:00
henry.chen
e7fdf6b1db chore(backup): backup blog with prefix: blog 2023-06-19 23:13:45 +08:00
Deepzz
305ae0ee70 Merge pull request #40 from eiblog/dependabot/go_modules/github.com/gin-gonic/gin-1.9.1
chore(deps): bump github.com/gin-gonic/gin from 1.9.0 to 1.9.1
2023-06-12 16:55:16 +08:00
dependabot[bot]
a5749fdab6 chore(deps): bump github.com/gin-gonic/gin from 1.9.0 to 1.9.1
Bumps [github.com/gin-gonic/gin](https://github.com/gin-gonic/gin) from 1.9.0 to 1.9.1.
- [Release notes](https://github.com/gin-gonic/gin/releases)
- [Changelog](https://github.com/gin-gonic/gin/blob/master/CHANGELOG.md)
- [Commits](https://github.com/gin-gonic/gin/compare/v1.9.0...v1.9.1)

---
updated-dependencies:
- dependency-name: github.com/gin-gonic/gin
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-06-01 20:35:02 +00:00
henry.chen
391f19e2a9 chore(release): 2.2.5 2023-05-26 09:57:35 +08:00
henry.chen
2c6c5bb977 fix(backup): libresolv.so.2: No such file or directory 2023-05-26 09:57:30 +08:00
henry.chen
860a85e209 chore: update go.mod 2023-05-25 16:46:26 +08:00
henry.chen
2a8d9b3bbd chore(release): 2.2.4 2023-05-25 16:44:34 +08:00
henry.chen
1509a68cda fix(ci): fix .dockerignore 2023-05-25 16:44:31 +08:00
henry.chen
7f26503247 chore(release): 2.2.3 2023-05-25 16:16:32 +08:00
henry.chen
235145ed46 chore(ci): print log 2023-05-25 16:16:28 +08:00
henry.chen
20e89d6a76 fix(ci): script file name 2023-05-25 16:08:45 +08:00
henry.chen
d86ab71ad9 chore(release): 2.2.2 2023-05-25 16:06:36 +08:00
henry.chen
4600ed5094 chore(ci): adjust ci 2023-05-25 16:06:31 +08:00
henry.chen
5fcadd1c81 chore(ci): golang version to 1.20 2023-05-17 15:58:44 +08:00
henry.chen
42b106d582 chore(release): 2.2.1 2023-05-17 15:49:04 +08:00
henry.chen
bb06be36fe fix: try to fix backup symbol not found 2023-05-17 15:49:01 +08:00
henry.chen
cb3fb2d2e7 chore(release): 2.2.0 2023-05-17 14:47:23 +08:00
henry.chen
779a23cb75 feat(backup): add restore flag 2023-05-17 14:42:28 +08:00
Deepzz
e2fa96cd62 Update docker-compose.yml 2023-05-15 08:58:56 +08:00
Deepzz
db26fb51e5 Merge pull request #37 from eiblog/dependabot/go_modules/github.com/gin-gonic/gin-1.9.0
chore(deps): bump github.com/gin-gonic/gin from 1.7.7 to 1.9.0
2023-05-06 09:09:41 +08:00
dependabot[bot]
994be5d508 chore(deps): bump github.com/gin-gonic/gin from 1.7.7 to 1.9.0
Bumps [github.com/gin-gonic/gin](https://github.com/gin-gonic/gin) from 1.7.7 to 1.9.0.
- [Release notes](https://github.com/gin-gonic/gin/releases)
- [Changelog](https://github.com/gin-gonic/gin/blob/master/CHANGELOG.md)
- [Commits](https://github.com/gin-gonic/gin/compare/v1.7.7...v1.9.0)

---
updated-dependencies:
- dependency-name: github.com/gin-gonic/gin
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-05-05 02:39:43 +00:00
henry.chen
a5c3d33565 chore: OpenSearch support mozilla firefox 2023-02-24 10:41:13 +08:00
Deepzz
1ffc58eccf Merge pull request #36 from eiblog/dependabot/go_modules/github.com/gin-gonic/gin-1.7.7
chore(deps): bump github.com/gin-gonic/gin from 1.7.4 to 1.7.7
2023-02-10 09:08:56 +08:00
dependabot[bot]
b6ad4e8949 chore(deps): bump github.com/gin-gonic/gin from 1.7.4 to 1.7.7
Bumps [github.com/gin-gonic/gin](https://github.com/gin-gonic/gin) from 1.7.4 to 1.7.7.
- [Release notes](https://github.com/gin-gonic/gin/releases)
- [Changelog](https://github.com/gin-gonic/gin/blob/master/CHANGELOG.md)
- [Commits](https://github.com/gin-gonic/gin/compare/v1.7.4...v1.7.7)

---
updated-dependencies:
- dependency-name: github.com/gin-gonic/gin
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-02-09 22:49:33 +00:00
henry.chen
41704917db chore(release): 2.1.18 2023-01-05 14:15:54 +08:00
henry.chen
f6ba716f55 fix(backup): can not execute 2023-01-05 14:15:46 +08:00
henry.chen
b2e6c168c5 fix: disqus api using http post 2023-01-05 13:51:18 +08:00
henry.chen
eca741896f chore(release): 2.1.17 2023-01-05 13:28:48 +08:00
henry.chen
17792e5a7e fix: fist comment of disqus error 2023-01-05 13:25:18 +08:00
henry.chen
04289c633e chore: optimize variable naming 2023-01-05 11:07:56 +08:00
henry.chen
3a5eb6fccc chore(release): 2.1.18 2023-01-05 09:17:39 +08:00
henry.chen
f6d8656c83 fix: 1. template read panic
2. optimization variable naming
2023-01-05 09:17:31 +08:00
henry.chen
4690d5123b chore(release): 2.1.17 2023-01-05 00:00:11 +08:00
henry.chen
a9e8e39d34 fix(disqus): failed to commit disqus comments 2023-01-04 23:58:50 +08:00
henry.chen
c51055a0db chore(release): 2.1.16 2022-11-20 23:40:06 +08:00
henry.chen
445b188517 chore: imgtonormal add at xmlTmpl 2022-11-20 23:37:49 +08:00
henry.chen
4bfff2e5e9 fix: rss image path incorrect: data-src -> src 2022-11-20 23:33:10 +08:00
Deepzz
aa91997c0c fix(backup): error path in compressed file 2022-10-14 14:45:15 +08:00
henry.chen
3b2a6689be chore: update 2022-10-01 21:18:53 +08:00
henry.chen
4c46be3f03 chore: update readme 2022-10-01 21:09:53 +08:00
henry.chen
da47e9880f chore: update readme 2022-09-30 09:51:44 +08:00
henry.chen
4f92e0d619 chore(release): 2.1.15 2022-09-28 19:00:36 +08:00
henry.chen
3a8f7d120b chore: rm dns with cgo 2022-09-28 19:00:26 +08:00
Deepzz
cf0a897ad0 chore(app.yml): default db use sqlite 2022-09-28 18:59:28 +08:00
henry.chen
418b604946 chore(release): 2.1.14 2022-09-28 18:20:41 +08:00
henry.chen
b93c320987 fix: cgo and sqlite build in alpine image closed #28 2022-09-28 18:20:38 +08:00
henry.chen
b24f7c0666 chore(release): 2.1.13 2022-09-27 10:50:22 +08:00
Deepzz
efe80fbc6b Update feedTpl.xml 2022-09-21 09:47:50 +08:00
henry.chen
65b89bcae5 chore(release): 2.1.12 2022-08-09 18:41:21 +08:00
henry.chen
289b8145bc fix: #35 no pubDate added for feed generation 2022-08-09 18:40:52 +08:00
henry.chen
bf0ad811ff chore(release): 2.1.11 2022-07-19 22:35:32 +08:00
henry.chen
db00a9b527 chore: upgrade to yaml.v3 close #1 2022-05-27 09:13:22 +08:00
henry.chen
38aa704198 fix(backup): configuration error 2022-05-11 10:16:38 +08:00
henry.chen
9c58447e3b chore(release): 2.1.10 2022-04-22 13:45:50 +08:00
Deepzz
34fc5f368c Update release.yml 2022-04-22 13:44:03 +08:00
Deepzz
daa561e67e Update eiblog.conf 2022-03-22 18:16:12 +08:00
henry.chen
364d293660 chore(release): 2.1.9 2022-02-14 17:50:42 +08:00
henry.chen
5170844623 fix(eiblog): comments title error 2022-01-10 15:50:13 +08:00
henry.chen
7047a3599d chore(docs): add docs images 2022-01-06 09:33:06 +08:00
Deepzz
d0c830c1e6 Create FUNDING.yml 2021-12-15 09:20:34 +08:00
henry.chen
bf699e4957 chore(release): 2.1.8 2021-11-18 10:47:10 +08:00
henry.chen
c315737871 chore: clean alpine respositories 2021-11-18 10:47:06 +08:00
henry.chen
d844c0e8f8 chore(release): 2.1.7 2021-11-13 22:21:10 +08:00
henry.chen
f6cb55c00f chore: update swag version & change uuid package 2021-11-13 22:20:48 +08:00
henry.chen
a5292027c0 chore(release): 2.1.6 2021-11-12 15:31:14 +08:00
henry.chen
00cf0b5c9f fix(backup): backup to qiniu, app.yml->validity change to int 2021-11-12 15:30:43 +08:00
henry.chen
6805afa759 chore(release): 2.1.5 2021-10-27 09:07:50 +08:00
henry.chen
4f6f85a85a fix(release): golang env 2021-10-27 09:06:37 +08:00
henry.chen
dc3e6659b5 chore(release): 2.1.4 2021-10-26 16:18:21 +08:00
henry.chen
ca74d13598 chore(release): 2.1.4 2021-10-26 16:18:00 +08:00
henry.chen
82fba0ddb4 fix(release): github action runner 2021-10-26 16:17:50 +08:00
henry.chen
2b6bae1f74 chore(release): 2.1.3 2021-10-15 16:54:47 +08:00
Deepzz
6387412776 Update README.md 2021-09-14 21:32:37 +08:00
razeen
cfaa25e123 fix(auto_save): fix auto save return empty id 2021-08-15 03:51:45 -07:00
Deepzz
6ff6acdbda Merge pull request #32 from eiblog/qiniu_zone
qiniu upload remove zone, support all zone upload
2021-08-12 09:19:56 +08:00
razeen
b3b3be448f chore(qiniu): qiniu upload remove zone, support all zone upload 2021-08-11 08:31:04 -07:00
henry.chen
78f5bfc1ce fix: backup app judge db driver 2021-08-11 18:44:35 +08:00
Deepzz
4b9eb1ff4d Merge pull request #29 from eiblog/fix_init_password
初始化地方参数错了。。。
2021-08-10 16:17:10 +08:00
Razeen
7d04b8f5c0 fix(pwd): fix init pwd 2021-08-10 10:37:42 +08:00
henry.chen
d000090e30 chore(release): 2.1.2 2021-07-27 17:19:04 +08:00
henry.chen
56e396f252 chore: restore app.yml database 2021-07-27 17:18:58 +08:00
henry.chen
f6662fa4c5 chore(release): 2.1.1 2021-07-27 09:15:17 +08:00
henry.chen
a570c783f3 chore(gin): upgrade gin to fix CVE-2020-28483 2021-07-27 09:15:03 +08:00
henry.chen
e2046d0d39 fix: sqlite error 2021-07-25 10:45:33 +08:00
henry.chen
cdbe082764 chore(docs): update backup.md 2021-07-14 12:10:40 +08:00
henry.chen
3a86c6a65c chore: clean go.sum 2021-07-14 10:57:37 +08:00
henry.chen
c1c9e6025a chore: add backup app 2021-07-14 10:54:30 +08:00
deepzz0
a15791a792 chore: clean dir 2021-05-27 13:54:12 +08:00
deepzz0
ea87f4b2e6 chore: update readme 2021-05-17 10:48:22 +08:00
deepzz0
a0653cf67f chore: update README.md 2021-05-16 11:22:13 +08:00
deepzz0
397d47bc00 fix: excerpt for es 2021-05-11 22:55:20 +08:00
deepzz0
00d9df4406 chore(release): 2.0.6 2021-05-08 11:22:28 +08:00
deepzz0
2b277bd901 fix: workdir loop 2021-05-08 11:19:31 +08:00
deepzz0
c18f3b7da9 fix: workdir path error 2021-05-08 10:54:21 +08:00
deepzz0
79ac024312 fix: archive page style 2021-05-07 16:28:26 +08:00
deepzz0
1e4c0afc19 fix: archive & serie 2021-05-07 16:25:34 +08:00
deepzz0
055d7d2164 fix: env PATH 2021-05-07 15:24:57 +08:00
deepzz0
ed7c510744 chore: update docs 2021-05-07 15:18:02 +08:00
deepzz0
488c0f04fe chore: mv conf/tpl->website/template 2021-05-07 15:17:20 +08:00
deepzz0
a99ed9504b chore: add adsense 2021-05-07 15:11:32 +08:00
deepzz0
6cde9323b1 chore: adjust config 2021-05-07 14:58:07 +08:00
deepzz0
2b226977ab chore: update eiblog.conf 2021-05-07 14:28:22 +08:00
deepzz0
aa076e5e3c fix: mongodb current close 2021-05-06 22:50:12 +08:00
deepzz0
26635790e1 chore: update docs 2021-05-06 15:32:32 +08:00
deepzz0
569a1fe2a1 chore: update 2021-05-06 15:29:22 +08:00
deepzz0
182eaff085 Merge branch 'v2' of github.com:eiblog/eiblog into v2 2021-05-06 11:29:37 +08:00
deepzz0
90e7082322 chore: update docs 2021-05-06 11:28:01 +08:00
Deepzz
e71e42571f Set theme jekyll-theme-cayman 2021-05-06 09:18:49 +08:00
deepzz0
3527e11b04 chore: update eiblog.conf 2021-05-06 09:07:27 +08:00
deepzz0
a407bdfe72 chore: update README.md 2021-05-05 20:52:48 +08:00
deepzz0
05aa254e70 chore: add eiblog.conf 2021-04-30 17:11:30 +08:00
deepzz0
4ec3065f9d fix: some bugs 2021-04-30 13:06:16 +08:00
deepzz0
7bec238f9c chore: add rdbms store 2021-04-30 11:01:03 +08:00
deepzz0
605787958d chore: remove some filed in app.yml 2021-04-30 09:50:33 +08:00
deepzz0
c06f02622a fix: tar file 2021-04-29 12:31:30 +08:00
deepzz0
f431bf5adc chore: alpine add tzdata 2021-04-29 11:44:45 +08:00
deepzz0
7922043bc6 chore: add clean 2021-04-29 11:18:31 +08:00
deepzz0
2fbc5fa024 chore: rename blog->eiblog 2021-04-29 11:17:20 +08:00
deepzz0
9c341f88d2 chore: update dockerignore 2021-04-29 10:59:52 +08:00
deepzz0
0ae5e6c9fc chore: default Dockerfile 2021-04-29 10:53:54 +08:00
deepzz0
3e2b746319 chore: rm workflow registry 2021-04-29 10:50:25 +08:00
deepzz0
2cd72e190e chore: update release.yml 2021-04-29 10:48:30 +08:00
deepzz0
1c955769f4 chore: renew github flow 2021-04-29 10:44:43 +08:00
deepzz0
a2769f0913 chore: rm tzdata 2021-04-28 16:34:29 +08:00
deepzz0
bf3d45a404 fix: filed name 2021-04-28 16:33:58 +08:00
deepzz0
4b92636079 fix: bugs 2021-04-28 16:28:14 +08:00
deepzz0
753aaa4ace fix: time location 2021-04-28 15:49:49 +08:00
deepzz0
43d7a26e19 fix: time in location 2021-04-28 15:37:10 +08:00
deepzz0
872d0b1987 chore: rm package utils 2021-04-28 15:10:13 +08:00
deepzz0
897b05d071 fix: ping func 2021-04-28 15:05:37 +08:00
deepzz0
c5a3bd6eab fix: delete trash article 2021-04-28 14:40:28 +08:00
deepzz0
beea4f1746 chore: tags use array 2021-04-28 12:50:06 +08:00
deepzz0
9f563f0ae9 chore: complete api 2021-04-28 11:57:37 +08:00
deepzz0
4749b1e681 chore: add api 2021-04-27 23:46:43 +08:00
deepzz0
a39e3aac3b chore: trash 2021-04-27 18:04:59 +08:00
deepzz0
63b55b2df8 chore: update html 2021-04-27 16:51:19 +08:00
deepzz0
cb091532d5 chore: update html 2021-04-27 16:22:23 +08:00
deepzz0
6b74e1d208 chore: rename field name 2021-04-27 16:16:32 +08:00
deepzz0
1815cea2cd fix: article list 2021-04-27 14:58:32 +08:00
deepzz0
c3fdbfcb78 chore: remove blogger config 2021-04-27 14:02:52 +08:00
deepzz0
990e6abbd8 chore: run blog 2021-04-27 11:08:55 +08:00
deepzz0
bb40570053 chore: mongodb unit test 2021-04-27 10:21:27 +08:00
deepzz0
e2df642a46 chore: store mongodb 2021-04-26 23:02:24 +08:00
deepzz0
68e01cdf1f refactor: eiblog 2021-04-26 15:51:16 +08:00
chenqijing
bd69c62254 chore: replace beian domain: https://beian.miit.gov.cn 2020-12-29 11:50:36 +08:00
chenqijing
970c6068d5 chore: update go.mod 2020-12-22 11:19:12 +08:00
chenqijing
27a1f600de chore: github actions 2020-12-22 11:09:55 +08:00
chenqijing
edf0fbac51 chore: rm .travis.yml 2020-12-17 21:02:48 +08:00
Deepzz
98ca570a36 Merge pull request #24 from eiblog/deepzz0-patch-1
Create release.yml
2020-12-17 20:58:58 +08:00
Deepzz
9a526f97f8 Create release.yml 2020-12-17 20:58:27 +08:00
chenqijing
33a29f5e57 chore: update twitter & print disqus email 2020-12-17 19:15:16 +08:00
henry.chen
30ebf76eda chore(release): 1.4.9 2019-12-18 19:33:08 +08:00
Deepzz
f5c0bcdb99 Merge pull request #23 from eiblog/fix_disqus
Fix disqus
2019-12-18 19:22:11 +08:00
henry.chen
4b53da3801 chore(disqus): rm app.yml/disqus/embed config 2019-12-18 19:17:23 +08:00
henry.chen
1bdfb6abea fix(disqus): connect reset by peer 2019-12-04 13:24:48 +08:00
henry.chen
33f47d8f3a Merge branch 'master' of github.com:eiblog/eiblog 2019-12-04 10:30:26 +08:00
deepzz0
cbd0cfaaf5 fix(blogroll): blogroll.html ul style 2019-11-09 23:45:28 +08:00
henry.chen
080c992a92 chore: update vendor 2019-09-30 15:30:10 +08:00
deepzz0
371b2326ea fix(qiniu): empty file name 2019-03-05 00:41:43 +08:00
deepzz0
2720d11b23 Merge branch 'master' of github.com:eiblog/eiblog 2019-03-03 15:06:33 +08:00
deepzz0
b7751d7b9e fix: qiniu upload file 2019-03-03 15:06:16 +08:00
henry.chen
24d81db8be add vendor & update README.md 2018-11-19 10:24:54 +08:00
deepzz0
010137ebf5 change admin avatar.jpg->avatar.png & fix duplicate id in html 2018-09-18 21:20:13 +08:00
deepzz0
c6a2439c54 use go1.11 with go mod 2018-08-25 18:29:00 +08:00
deepzz0
1d54ff3ac5 fix modify blogroll.html and about.html archived in archive.html 2018-07-17 22:11:11 +08:00
henry.chen
63a4d69209 nginx config: use Expect-CT repleace HPKP 2018-07-17 10:32:54 +08:00
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
henry.chen
d02c838447 fix archive page bug 2018-01-25 23:09:59 +08:00
Deepzz
d17acf5325 Update amusing.md 2018-01-17 19:16:08 +08:00
deepzz0
b278ca377f update changelog.md 2018-01-14 13:53:32 +08:00
deepzz0
93131441e4 update 2018-01-14 13:38:26 +08:00
deepzz0
ddcc6c2d2e auto archiving by year when the month great than 12 2018-01-14 13:12:59 +08:00
henry.chen
ef63ae9598 fix page archive unable auto update 2018-01-14 02:40:11 +08:00
henry.chen
2ed9db5c7b code logical adjust 2018-01-14 02:02:12 +08:00
deepzz0
06a12bc6f9 update vendor 2018-01-13 18:23:03 +08:00
deepzz0
6524b45751 adjust the code 2018-01-13 18:19:54 +08:00
henry.chen
ceb9e2690b 添加 disqus thread 创建接口 2018-01-13 02:56:35 +08:00
deepzz0
405fbaf24f fix can delete blogroll and about page & fix delete and readd article bug 2018-01-07 20:30:14 +08:00
deepzz0
3245c0e0d3 update vendor & fix upload file url & fix judge file type 2018-01-06 23:24:27 +08:00
Deepzz
badc62e3f0 Update README.md 2018-01-06 11:47:20 +08:00
deepzz0
a5561f257b comment docker-compose.yml backup 2018-01-02 20:21:45 +08:00
deepzz0
eb37b83ebd update README.md 2018-01-01 19:03:16 +08:00
deepzz0
b2fab703fc Merge branch 'master' of github.com:eiblog/eiblog 2018-01-01 18:59:30 +08:00
deepzz0
37deb390d9 docker-compose.yml 添加数据库备份镜像 2018-01-01 18:59:10 +08:00
Deepzz
6fa5088352 更新 ct 服务器地址 2017-12-30 13:50:19 +08:00
Deepzz
e023a33786 Update app.yml
移除 disqus 评论及 Google 分析私人信息配置
2017-12-08 12:19:01 +08:00
henry.chen
6f818c4b5d fix search.html <no value> 2017-12-05 15:08:32 +08:00
henry.chen
9ad22fb2d9 don't use dynamic link: CGO_ENABLED=0 2017-11-30 10:04:54 +08:00
henry.chen
fc37d5e093 fix page:admin/write-post autocomplete tag 2017-11-29 16:17:58 +08:00
henry.chen
61024bfebd update 2017-11-27 18:34:03 +08:00
henry.chen
f20c4a6063 fix docker image: exec user process caused "no such file or directory" 2017-11-27 18:17:41 +08:00
henry.chen
c24e6bf7bd update .travis.yml 2017-11-27 16:43:30 +08:00
henry.chen
ade94168d3 update .travis.yml 2017-11-27 16:32:39 +08:00
henry.chen
552d010650 fix background turn page 2017-11-27 15:21:28 +08:00
deepzz0
1c3106cbb0 update vendor 2017-11-24 22:58:59 +08:00
henry.chen
168937f1b2 fix gopkg.in/mgo import conflict 2017-11-23 13:57:20 +08:00
henry.chen
730cffcb5b 修复文章自动保存bug+发布文章不成功bug 2017-11-17 13:30:47 +08:00
deepzz0
8c3f1c2aba update travis.yml 2017-11-05 13:03:52 +08:00
deepzz0
ea375ea76c update travis.yml 2017-11-05 12:56:46 +08:00
deepzz0
275a6c0c31 update travis.yml 2017-11-05 12:46:01 +08:00
deepzz0
360204995d 使用github的七牛SDK,配置名称Kodo->Qiniu 2017-11-05 12:27:22 +08:00
deepzz0
c9fc0cc75a Merge branch 'master' of github.com:eiblog/eiblog 2017-10-19 20:23:45 +08:00
deepzz0
41daaa322e fix mod date panic 2017-10-19 20:23:36 +08:00
Deepzz
894535fbe5 Update README.md 2017-10-10 20:16:01 -05:00
Deepzz
6fc5af1b0f Update eiblog.conf 2017-09-26 22:42:49 -05:00
henry.chen
5ce806a7d7 挑战 acme.sh 文件验证路径 2017-08-25 18:01:37 +08:00
Deepzz
25cb23fdb3 Update README.md 2017-08-20 17:48:44 +08:00
deepzz0
a89a1a2bc9 update 2017-08-19 14:26:19 +08:00
deepzz0
93e170f9ac fix es/config/scripts 2017-08-19 14:25:28 +08:00
Deepzz
59d9a616aa Update README.md 2017-08-17 17:00:52 +08:00
Deepzz
2ff0934206 Update install.md 2017-08-15 21:31:34 +08:00
Deepzz
cde7cba2f0 Update README.md 2017-08-15 21:23:12 +08:00
Deepzz
2be7501afe Update README.md 2017-08-15 21:12:03 +08:00
deepzz0
487d35dae2 add comments 2017-08-08 20:59:45 +08:00
henry.chen
19af9376cb add comments 2017-08-08 12:45:58 +08:00
deepzz0
3ddd2a0b33 fix disqus 基础评论bug 2017-08-08 01:03:10 +08:00
Deepzz
ee7523b124 Update helper.go 2017-08-07 18:07:58 +08:00
Deepzz
cc1dbac1f0 clean eiblog.conf 2017-07-27 22:03:43 +08:00
deepzz0
04532ba8a6 fix conflict 2017-07-26 22:48:16 +08:00
deepzz0
0a2a132b11 rm some cod in domain.cnf 2017-07-26 22:45:54 +08:00
Deepzz
3ff712d407 Update eiblog.conf 2017-07-25 09:22:08 +08:00
deepzz0
27162d2205 fix unuse tag <!--more-->
intercept errors
2017-07-15 13:46:29 +08:00
deepzz0
f150974566 rm .travis.yml about glide 2017-07-13 21:29:23 +08:00
deepzz0
b94fc825b3 updage docs 2017-07-13 21:23:28 +08:00
deepzz0
d8f0e30285 update 2017-07-13 20:31:00 +08:00
deepzz0
e0a5f0ebca avatar img use cache 2017-07-13 01:37:35 +08:00
deepzz0
c18d9c0bef update vendor 2017-07-11 23:50:01 +08:00
deepzz0
e1ec5cd08a update vendor 2017-07-09 03:33:28 +08:00
deepzz0
5efdd72e58 update docs 2017-07-09 03:24:46 +08:00
deepzz0
b9470fa14c fix and update config 2017-07-09 01:18:07 +08:00
deepzz0
a932d2906d update .dockerignore 2017-07-09 00:27:46 +08:00
deepzz0
3ff5977941 update vendor and use single static 2017-07-08 21:54:39 +08:00
deepzz0
da7b726e8d rename mode.domain-> mode.domains 2017-07-08 16:29:47 +08:00
deepzz0
3923bc70f1 update vendor 2017-07-08 12:16:15 +08:00
deepzz0
8dc73fd67c replace cert pin-sha256: trustasai G6 -> trustasia G5 2017-07-06 23:28:04 +08:00
deepzz0
2825bbfeae 完善 disqus 评论 2017-06-26 23:51:13 +08:00
deepzz0
66811830b0 update Makefile 2017-06-25 15:22:33 +08:00
deepzz0
c4bf59ce5d update 2017-06-25 06:51:44 +08:00
deepzz0
6cea283f86 添加 Makefile,使用 acme.sh 自动更新证书 2017-06-25 06:48:19 +08:00
deepzz0
3992db49ba update 2017-06-24 20:58:21 +08:00
deepzz0
055c2307cb update config 2017-06-24 19:21:53 +08:00
Deepzz
11b0f486cd Delete goblog.conf 2017-06-24 16:56:42 +08:00
Deepzz
ed0a50b626 Update eiblog.conf 2017-06-24 16:34:22 +08:00
deepzz0
cf2b2d6d34 rename 2017-06-24 16:12:00 +08:00
deepzz0
00456806bb 添加双证书配置 2017-06-24 16:11:28 +08:00
deepzz0
54f5289d6b release v1.20 2017-06-14 21:25:18 +08:00
deepzz0
1634418a13 fix disqus bug 2017-06-12 01:44:28 +08:00
deepzz0
a84a474504 Merge branch 'master' of github.com:eiblog/eiblog 2017-06-12 00:20:10 +08:00
deepzz0
b64cf5985a fix disqus bug 2017-06-12 00:19:17 +08:00
Deepzz
4f9965b6bd Update README.md 2017-05-26 08:59:57 +08:00
Deepzz
daffa6c294 Update CHANGELOG.md 2017-05-12 22:07:05 +08:00
Deepzz
0bd738438e Update install.md 2017-05-12 22:03:19 +08:00
Deepzz
309339492c Update README.md 2017-05-05 18:10:43 +08:00
Deepzz
694036c65f Update README.md 2017-05-05 18:08:36 +08:00
Deepzz
cafdaac9f4 Update README.md 2017-05-05 18:07:20 +08:00
henry.chen
f6956f592f update 2017-04-28 18:01:19 +08:00
deepzz0
2382f76cf6 merge 2017-04-22 10:50:06 +08:00
deepzz0
a66a3c0198 update disqus.js->disqus_921d24.js 2017-04-22 10:47:01 +08:00
henry.chen
31c398700e 将配置分离出来 2017-04-03 10:43:40 +08:00
henry.chen
9296147a0f update 2017-04-03 01:18:46 +08:00
henry.chen
6932799cba update disqus_78bca4.js 2017-04-01 15:53:29 +08:00
Deepzz
b1ff8b59af Update README.md 2017-03-18 15:42:18 +08:00
henry.chen
5f047c2c27 update vendor directory 2017-03-11 00:54:27 +08:00
Deepzz
5d24af11e5 Update front.go 2017-03-10 09:27:34 +08:00
deepzz0
d03f327fb4 update 2017-03-08 20:45:47 +08:00
deepzz0
ef9e64469b use go1.8 2017-03-08 20:18:13 +08:00
deepzz0
86e7374997 update 2017-03-02 23:08:22 +08:00
deepzz0
4f24b80107 fix out of range about MapArticles 2017-03-02 23:07:36 +08:00
deepzz0
2c49a1ec8d Merge branch 'master' of github.com:eiblog/eiblog 2017-03-02 22:48:03 +08:00
deepzz0
3eaab0fb1f fix out of range about MapArticels 2017-03-02 22:47:44 +08:00
Deepzz
8e6404a90a Update writing.md 2017-02-25 22:47:53 +08:00
deepzz0
7775ea35a2 update docs 2017-02-25 22:42:28 +08:00
deepzz0
931d7b0683 document filing 2017-02-25 19:35:23 +08:00
deepzz0
562f4d86c6 add vendor 2017-02-18 15:23:57 +08:00
deepzz0
4ebbc38cc0 pretty *.xml 2017-02-17 23:53:07 +08:00
deepzz0
9509cd66e6 delete static/*.xml 2017-02-17 23:36:56 +08:00
deepzz0
48756a2810 generate by xml.go 2017-02-17 23:35:51 +08:00
deepzz0
ec8297c3f6 fix article desc error 2017-02-11 19:37:51 +08:00
deepzz0
d622a8397f update 2017-02-07 20:16:51 +08:00
deepzz0
c75619785d Merge branch 'master' of github.com:eiblog/eiblog 2017-02-02 10:49:22 +08:00
deepzz0
c014c6450b replace jquery 2017-02-02 10:48:43 +08:00
Deepzz
d8879f8d32 Update README.md 2017-01-17 21:33:52 +08:00
deepzz0
9bb0905aab simple article description 2017-01-11 01:19:59 +08:00
deepzz0
c39844ca63 update .travis.yml 2017-01-09 23:28:21 +08:00
233 changed files with 7758 additions and 707808 deletions

View File

@@ -1,17 +1,12 @@
.git
vendor
setting
conf
static
views
!static/tzdata
Dockerfile
glide.yaml
glide.lock
*.yml
*.md
*.go
*.sh
*.DS_Store
# Ignore all files and dirs
# Unignore files or dirs
.github
bin
docs
.gitignore
.dockerignore
db.sqlite
docker-compose.yml
eiblog.conf
Makefile

12
.github/FUNDING.yml vendored Normal file
View File

@@ -0,0 +1,12 @@
# These are supported funding model platforms
github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
patreon: # Replace with a single Patreon username
open_collective: eiblog
ko_fi: # Replace with a single Ko-fi username
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
liberapay: # Replace with a single Liberapay username
issuehunt: # Replace with a single IssueHunt username
otechie: # Replace with a single Otechie username
custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']

169
.github/workflows/release.yml vendored Normal file
View File

@@ -0,0 +1,169 @@
name: Release Image & Asset
on:
push:
tags:
- "v*"
permissions:
contents: write
packages: write
id-token: write # for SLSA provenance
attestations: write # for attestations
env:
REGISTRY: docker.io
GOPROXY: https://goproxy.io,direct
jobs:
# Job 1: 打包源码 tar 文件
package-source:
runs-on: ubuntu-latest
outputs:
tag: ${{ steps.vars.outputs.tag }}
sha: ${{ steps.vars.outputs.sha }}
date: ${{ steps.vars.outputs.date }}
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Set up Go
uses: actions/setup-go@v5
with:
go-version-file: go.mod
cache: true
- name: Extract metadata
id: vars
run: |
echo "tag=${GITHUB_REF#refs/tags/}" >> $GITHUB_OUTPUT
echo "sha=${GITHUB_SHA::8}" >> $GITHUB_OUTPUT
echo "date=$(date -u +'%Y-%m-%dT%H:%M:%SZ')" >> $GITHUB_OUTPUT
- name: Package tar archive
run: scripts/dist_tar.sh ${{ steps.vars.outputs.tag }}
- name: Upload tar artifacts
uses: actions/upload-artifact@v4
with:
name: release-archives
path: "*.tar.gz"
retention-days: 7
# Job 2: 构建并推送 Docker 镜像
build-images:
runs-on: ubuntu-latest
needs: package-source
strategy:
fail-fast: false
matrix:
app: [eiblog, backup]
include:
- app: eiblog
port: 9000
- app: backup
port: 9001
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
with:
platforms: linux/amd64,linux/arm64,linux/arm/v7
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Login to Docker Hub
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
- name: Extract metadata for Docker
id: meta
uses: docker/metadata-action@v5
with:
images: ${{ env.REGISTRY }}/deepzz0/${{ matrix.app }}
tags: |
type=ref,event=tag
type=raw,value=latest,enable={{is_default_branch}}
labels: |
org.opencontainers.image.title=${{ matrix.app }}
org.opencontainers.image.description=eiblog ${{ matrix.app }} service
org.opencontainers.image.vendor=deepzz
org.opencontainers.image.revision=${{ needs.package-source.outputs.sha }}
org.opencontainers.image.created=${{ needs.package-source.outputs.date }}
- name: Build and push ${{ matrix.app }} image
id: build
uses: docker/build-push-action@v5
with:
context: .
file: ./build/package/${{ matrix.app }}/Dockerfile
platforms: linux/amd64,linux/arm64,linux/arm/v7
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
cache-from: type=gha,scope=${{ matrix.app }}
cache-to: type=gha,mode=max,scope=${{ matrix.app }}
provenance: true
sbom: true
- name: Generate SLSA attestation
uses: actions/attest-build-provenance@v1
with:
subject-name: ${{ env.REGISTRY }}/deepzz0/${{ matrix.app }}
subject-digest: ${{ steps.build.outputs.digest }}
# Job 3: 创建 GitHub Release
create-release:
runs-on: ubuntu-latest
needs: [package-source, build-images]
steps:
- name: Download tar artifacts
uses: actions/download-artifact@v4
with:
name: release-archives
- name: Create release summary
run: |
cat >> $GITHUB_STEP_SUMMARY << 'EOF'
# 🎉 Release ${{ needs.package-source.outputs.tag }} Created!
## 📦 Docker Images
- **eiblog**: `deepzz0/eiblog:${{ needs.package-source.outputs.tag }}`
- **backup**: `deepzz0/backup:${{ needs.package-source.outputs.tag }}`
## 🏗️ Build Info
- **Tag**: ${{ needs.package-source.outputs.tag }}
- **Commit**: ${{ needs.package-source.outputs.sha }}
- **Built**: ${{ needs.package-source.outputs.date }}
- **Platforms**: linux/amd64, linux/arm64, linux/arm/v7
## 🔐 Security
- ✅ SLSA Build Provenance
- ✅ SBOM (Software Bill of Materials)
- ✅ Container Signing
EOF
- name: Create GitHub release
uses: softprops/action-gh-release@v2
with:
files: "*.tar.gz"
generate_release_notes: true
name: Release ${{ needs.package-source.outputs.tag }}
body: |
## Docker Images
```bash
docker pull deepzz0/eiblog:${{ needs.package-source.outputs.tag }}
docker pull deepzz0/backup:${{ needs.package-source.outputs.tag }}
```
## Multi-Architecture Support
- linux/amd64
- linux/arm64
- linux/arm/v7
Built with commit ${{ needs.package-source.outputs.sha }} on ${{ needs.package-source.outputs.date }}

29
.gitignore vendored
View File

@@ -1,10 +1,23 @@
*.DS_Store
# Binaries for programs and plugins
*.exe
vendor
vendor/**
conf/ssl/domain.*
eiblog
static/feed.xml
static/opensearch.xml
static/sitemap.xml
*.exe~
*.dll
*.so
*.dylib
*.DS_Store
*.tar.gz
*.db
# 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
cmd/eiblog/etc/assets/*.xml
cmd/eiblog/etc/assets/*.txt
db.sqlite
cmd/*/backend

View File

@@ -1,44 +0,0 @@
sudo: required # 超级权限
dist: trusty # 在ubuntu:trusty
language: go # 声明构建语言环境
go: # 只构建最新版本
- tip
services: # docker环境
- docker
# branches: # 限定项目分支
# only:
# - master
before_install:
- curl https://glide.sh/get | sh # 安装glide包管理
script:
- glide up
- GOOS=linux GOARCH=amd64 go build # 编译版本
- docker build -t registry.cn-hangzhou.aliyuncs.com/deepzz/eiblog . # 构建镜像
after_success:
- if [ "$TRAVIS_BRANCH" =~ ^v[0-9](\.[0-9])+.*$ ]; then
docker login -u="$DOCKER_USERNAME" -p="$DOCKER_PASSWORD" registry.cn-hangzhou.aliyuncs.com;
docker push registry.cn-hangzhou.aliyuncs.com/deepzz/eiblog;
fi
before_deploy:
- ./dist.sh
deploy:
provider: releases
api_key:
secure: AGW05sQuQfjy77+JprSV+ohti/VVgFuh7UOTV0+hwxqsOVXSoIQz/ZPOlHWPP1iiSiGGEalspm+UtKRvADcDfllUaEwo7kebfFeMx4X//qxFxQSQ5LJYx7qxsTDpuQ4CF8zifCtND3ynnUAdx0P6FFkxE/67kN2n4CrhIxYCUb8gNPzDDRuS0ZNBC4zzNldJo/vtatbvc2btuFfwKoClYf+xPLy5luLqDvKF+hdjJ8NuZl8BWkWxXE+kk8fW4iUn2IV0qtLRZ3FQUyAF2CumzxqZfViX+rYTXsfbabYY5nYG6opT4mUEF58T4X3uRV0e3Q6Fe73nmLh9cyAoQl1BTSJ1XiyV4eJJWcKEMY7DqJ646lzoUT449YwvTK57klcfBbShpcjFf2alVdEbr9jbEXrCkuWKnssO9VfufhYF6t9h22c79evpexpIbsoncPD+b+n712MzufREtUF4kpUdkIir5n9CgQl/l7S+fV+n+gME+mcA44K7iPXkC80UfxJiw83QizT39OQhExq6SPIwrbt2vlAkBpSLMUS9iAHtTJYUsmH1SsmrxGK3WromKysWeTRJbcAJls2k6V313sn4TuYBWiHTUfsUBhv+objDFA2TsfO+g0g1JsdfZb5EsKrqNvs/2ta1xlzdE0+/TLG/YNKIOPkHnXswAM3DZm3zEss=
skip_cleanup: true
file: "*.tar.gz"
file_glob: true
on:
tags: true
repo: eiblog/eiblog
all_branches: true

View File

@@ -1,12 +1,315 @@
# Eiblog Changelog
# Changelog
## v1.0.0 (2016-01-09)
首次发布版本
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.
* 全站`HTTPS`设计,安全、极速。
* `Elasticsearch`博客搜索系统。
* 开源`Typecho`完整博客后台。
* 全功能`Markdown`编辑器。
* 异步`Google analysts`分析统计。
* `Disqus`评论系统。
* 后台直接对接七牛`CDN`
### [3.0.10](https://github.com/eiblog/eiblog/compare/v3.0.9...v3.0.10) (2025-11-12)
### Bug Fixes
* default RUN_MODE=prod ([5aa8152](https://github.com/eiblog/eiblog/commit/5aa81528027eca648e430503a1f5a04118ca650e))
### [3.0.9](https://github.com/eiblog/eiblog/compare/v3.0.8...v3.0.9) (2025-11-12)
### Bug Fixes
* image RUN_MODE ([71c0226](https://github.com/eiblog/eiblog/commit/71c02266bc0173a4af1ffc876dc36051377e1af0))
### [3.0.8](https://github.com/eiblog/eiblog/compare/v3.0.7...v3.0.8) (2025-10-14)
### Bug Fixes
* upload file path ([523ee64](https://github.com/eiblog/eiblog/commit/523ee64931a0dc1f9f743682fe84b1a1f250686c))
### [3.0.7](https://github.com/eiblog/eiblog/compare/v3.0.6...v3.0.7) (2025-07-26)
### Bug Fixes
* feed not generate ([11b22da](https://github.com/eiblog/eiblog/commit/11b22da339c542eb6d697cb3ac7bc78a401c6420))
### [3.0.6](https://github.com/eiblog/eiblog/compare/v3.0.5...v3.0.6) (2025-07-25)
### Bug Fixes
* feed & sitemap not generate ([629ad78](https://github.com/eiblog/eiblog/commit/629ad782c45fae7af9efdf99513bafdf87e7758c))
### [3.0.5](https://github.com/eiblog/eiblog/compare/v3.0.4...v3.0.5) (2025-07-25)
### Bug Fixes
* admin login session ([c1d73f1](https://github.com/eiblog/eiblog/commit/c1d73f1a453af62d15c25a79c382d9cefd8a3d2e))
* mongodb uri error ([33afbd3](https://github.com/eiblog/eiblog/commit/33afbd351d2b41f9edf36959908c3f183745d903))
* RUN_MODE error ([eaeeaaa](https://github.com/eiblog/eiblog/commit/eaeeaaafb98a4aa0a42dba74b411b1d361faf1d5))
### [3.0.4](https://github.com/eiblog/eiblog/compare/v3.0.3...v3.0.4) (2025-07-25)
### Bug Fixes
* **ci:** golang version ([9f77fb7](https://github.com/eiblog/eiblog/commit/9f77fb7b3f283aacb9d9c2a0e2f79c8a6c412edb))
### [3.0.2](https://github.com/eiblog/eiblog/compare/v3.0.1...v3.0.2) (2025-07-25)
### Bug Fixes
* ci ([ccb5e45](https://github.com/eiblog/eiblog/commit/ccb5e4546e224182c949e72e9eae82fbbe1a02fe))
### [3.0.1](https://github.com/eiblog/eiblog/compare/v3.0.0...v3.0.1) (2025-07-25)
### Bug Fixes
* dist tar ([cb2ed7c](https://github.com/eiblog/eiblog/commit/cb2ed7cb8244dda8cbd8c5966c7ed02e177777e5))
## [3.0.0](https://github.com/eiblog/eiblog/compare/v2.2.17...v3.0.0) (2025-07-24)
### Features
* support custom page ([b69248f](https://github.com/eiblog/eiblog/commit/b69248f6a4937f8157d2393bd44c6c55174ae3e7))
* twofactor bind ([e5100fa](https://github.com/eiblog/eiblog/commit/e5100fa018e2955c3d649b70245e3a379658d7ba))
### Bug Fixes
* ci ([f4c70b4](https://github.com/eiblog/eiblog/commit/f4c70b46c19d8761c252b767c92016c195299c0c))
* ci dist tar ([24bfe52](https://github.com/eiblog/eiblog/commit/24bfe528b28b995c3d5b2c13314c8849a036942b))
* custom page support embed ([aee4194](https://github.com/eiblog/eiblog/commit/aee4194c71dd98a7a84da96810a060f716d1ea57))
### [2.2.16](https://github.com/eiblog/eiblog/compare/v2.2.15...v2.2.16) (2025-03-13)
### Bug Fixes
* empty "beian" display issue ([79fccb9](https://github.com/eiblog/eiblog/commit/79fccb958c3eb8e21615389359c1d613c5fb079b))
### [2.2.15](https://github.com/eiblog/eiblog/compare/v2.2.14...v2.2.15) (2024-12-31)
### Bug Fixes
* disqus list posts ([52fe730](https://github.com/eiblog/eiblog/commit/52fe7303f3345421c0f2e2989a6c174d5b1a689e))
* disqus thread not store ([616248d](https://github.com/eiblog/eiblog/commit/616248d33fdf44dbc3aed41e92adae001a4f5577))
### [2.2.14](https://github.com/eiblog/eiblog/compare/v2.2.13...v2.2.14) (2024-10-10)
### Bug Fixes
* 1. bei_an cannot update error ([b53fc91](https://github.com/eiblog/eiblog/commit/b53fc91ce7b67c3811c232ad6236898f84bc391b)), closes [#43](https://github.com/eiblog/eiblog/issues/43) [#44](https://github.com/eiblog/eiblog/issues/44)
* **serie:** update serie did not rerender ([88f23bd](https://github.com/eiblog/eiblog/commit/88f23bd1a0d6183d6de484cb79303506f0506d15))
### [2.2.13](https://github.com/eiblog/eiblog/compare/v2.2.12...v2.2.13) (2024-01-02)
### Bug Fixes
* load more comments ([95e55ee](https://github.com/eiblog/eiblog/commit/95e55ee13c1af13e2bb2149ccbe60013c93e4e69))
### [2.2.12](https://github.com/eiblog/eiblog/compare/v2.2.11...v2.2.12) (2024-01-02)
### [2.2.11](https://github.com/eiblog/eiblog/compare/v2.2.10...v2.2.11) (2024-01-02)
### Bug Fixes
* **disqus:** fix returned posts list not have parent post ([9d71ca8](https://github.com/eiblog/eiblog/commit/9d71ca81988bfc614d13fcb02079f0dba9ef43cc))
### [2.2.10](https://github.com/eiblog/eiblog/compare/v2.2.9...v2.2.10) (2023-12-22)
### [2.2.9](https://github.com/eiblog/eiblog/compare/v2.2.8...v2.2.9) (2023-09-25)
### Bug Fixes
* google analytics not work, supported ga4 measurement protocol ([9b918ca](https://github.com/eiblog/eiblog/commit/9b918caccd9fd7faff7d253693e075ccd0150c17))
### [2.2.8](https://github.com/eiblog/eiblog/compare/v2.2.7...v2.2.8) (2023-07-12)
### Bug Fixes
* **backup:** restore db and tests ([f28d0e7](https://github.com/eiblog/eiblog/commit/f28d0e77e06dd435dc13a1867f18a011e34b8f53))
### [2.2.7](https://github.com/eiblog/eiblog/compare/v2.2.6...v2.2.7) (2023-07-12)
### Bug Fixes
* **backup:** 数据恢复错误 ([ee51f67](https://github.com/eiblog/eiblog/commit/ee51f678cbf93332fedccf927704e78a6063289c))
### [2.2.6](https://github.com/eiblog/eiblog/compare/v2.2.5...v2.2.6) (2023-06-19)
### [2.2.5](https://github.com/eiblog/eiblog/compare/v2.2.4...v2.2.5) (2023-05-26)
### Bug Fixes
* **backup:** libresolv.so.2: No such file or directory ([2c6c5bb](https://github.com/eiblog/eiblog/commit/2c6c5bb97708683bed8c889a7a1076f1f88ee5a8))
### [2.2.4](https://github.com/eiblog/eiblog/compare/v2.2.3...v2.2.4) (2023-05-25)
### Bug Fixes
* **ci:** fix .dockerignore ([1509a68](https://github.com/eiblog/eiblog/commit/1509a68cda74ace6b4060b5015f20143303ca36f))
### [2.2.3](https://github.com/eiblog/eiblog/compare/v2.2.2...v2.2.3) (2023-05-25)
### Bug Fixes
* **ci:** script file name ([20e89d6](https://github.com/eiblog/eiblog/commit/20e89d6a76f01aee60b98f8ae43a2c65b4fb3904))
### [2.2.2](https://github.com/eiblog/eiblog/compare/v2.2.1...v2.2.2) (2023-05-25)
### [2.2.1](https://github.com/eiblog/eiblog/compare/v2.2.0...v2.2.1) (2023-05-17)
### Bug Fixes
* try to fix backup symbol not found ([bb06be3](https://github.com/eiblog/eiblog/commit/bb06be36fe016e753ca56aa2321ce7e39bffe3e0))
## [2.2.0](https://github.com/eiblog/eiblog/compare/v2.1.18...v2.2.0) (2023-05-17)
### Features
* **backup:** add restore flag ([779a23c](https://github.com/eiblog/eiblog/commit/779a23cb75ab5059826370d08b754569a4af4aea))
### [2.1.18](https://github.com/eiblog/eiblog/compare/v2.1.17...v2.1.18) (2023-01-05)
### Bug Fixes
* **backup:** can not execute ([f6ba716](https://github.com/eiblog/eiblog/commit/f6ba716f554cfb638752875c4842e4ffb1b7e9a6))
* disqus api using http post ([b2e6c16](https://github.com/eiblog/eiblog/commit/b2e6c168c5f63b29cf5c2884e04dd99caa677bc0))
### [2.1.17](https://github.com/eiblog/eiblog/compare/v2.1.16...v2.1.17) (2023-01-05)
### Bug Fixes
* 1. template read panic ([f6d8656](https://github.com/eiblog/eiblog/commit/f6d8656c83591584581383643d109611d7ed2caa))
* **disqus:** failed to commit disqus comments ([a9e8e39](https://github.com/eiblog/eiblog/commit/a9e8e39d342488ec46175997f3df9ab109f2fecf))
* fist comment of disqus error ([17792e5](https://github.com/eiblog/eiblog/commit/17792e5a7edb7e84623d9307555e7983ba306565))
### [2.1.16](https://github.com/eiblog/eiblog/compare/v2.1.15...v2.1.16) (2022-11-20)
### Bug Fixes
* **backup:** error path in compressed file ([aa91997](https://github.com/eiblog/eiblog/commit/aa91997c0caca27e9979692879f8877765dabd9d))
* rss image path incorrect: data-src -> src ([4bfff2e](https://github.com/eiblog/eiblog/commit/4bfff2e5e9b0efb4112a5f2f6bc55eebcef1c6eb))
### [2.1.15](https://github.com/eiblog/eiblog/compare/v2.1.14...v2.1.15) (2022-09-28)
### [2.1.14](https://github.com/eiblog/eiblog/compare/v2.1.13...v2.1.14) (2022-09-28)
### Bug Fixes
* cgo and sqlite build in alpine image closed [#28](https://github.com/eiblog/eiblog/issues/28) ([b93c320](https://github.com/eiblog/eiblog/commit/b93c320987a936db6e5ca50c547022de9ab9a0f1))
### [2.1.13](https://github.com/eiblog/eiblog/compare/v2.1.12...v2.1.13) (2022-09-27)
### [2.1.12](https://github.com/eiblog/eiblog/compare/v2.1.11...v2.1.12) (2022-08-09)
### Bug Fixes
* [#35](https://github.com/eiblog/eiblog/issues/35) no pubDate added for feed generation ([289b814](https://github.com/eiblog/eiblog/commit/289b8145bcdabd0060c9a0d5f40f5df69d3882d3))
### [2.1.11](https://github.com/eiblog/eiblog/compare/v2.1.10...v2.1.11) (2022-07-19)
### Bug Fixes
* **backup:** configuration error ([38aa704](https://github.com/eiblog/eiblog/commit/38aa704198070d3e1436b230b40b1deb3e94c5ac))
### [2.1.10](https://github.com/eiblog/eiblog/compare/v2.1.9...v2.1.10) (2022-04-22)
### [2.1.9](https://github.com/eiblog/eiblog/compare/v2.1.8...v2.1.9) (2022-02-14)
### Bug Fixes
* **eiblog:** comments title error ([5170844](https://github.com/eiblog/eiblog/commit/517084462336ce01c3f79099c1e54297979f5877))
### [2.1.8](https://github.com/eiblog/eiblog/compare/v2.1.7...v2.1.8) (2021-11-18)
### [2.1.7](https://github.com/eiblog/eiblog/compare/v2.1.6...v2.1.7) (2021-11-13)
### [2.1.6](https://github.com/eiblog/eiblog/compare/v2.1.5...v2.1.6) (2021-11-12)
### Bug Fixes
* **backup:** backup to qiniu, app.yml->validity change to int ([00cf0b5](https://github.com/eiblog/eiblog/commit/00cf0b5c9f787f3f45f1747b7cb1742c417c6dd6))
### [2.1.5](https://github.com/eiblog/eiblog/compare/v2.1.4...v2.1.5) (2021-10-27)
### Bug Fixes
* **release:** golang env ([4f6f85a](https://github.com/eiblog/eiblog/commit/4f6f85a85ae56849c49e91d76bbbce1790f16e29))
### [2.1.4](https://github.com/eiblog/eiblog/compare/v2.1.3...v2.1.4) (2021-10-26)
### Bug Fixes
* **release:** github action runner ([82fba0d](https://github.com/eiblog/eiblog/commit/82fba0ddb47c1f66cb659f775c120c08dd2b4447))
### [2.1.4](https://github.com/eiblog/eiblog/compare/v2.1.3...v2.1.4) (2021-10-26)
### Bug Fixes
* **release:** github action runner ([82fba0d](https://github.com/eiblog/eiblog/commit/82fba0ddb47c1f66cb659f775c120c08dd2b4447))
### [2.1.3](https://github.com/eiblog/eiblog/compare/v2.1.2...v2.1.3) (2021-10-15)
### Bug Fixes
* **auto_save:** fix auto save return empty id ([cfaa25e](https://github.com/eiblog/eiblog/commit/cfaa25e1239aba580e0718d40f1a2bf31158b217))
* backup app judge db driver ([78f5bfc](https://github.com/eiblog/eiblog/commit/78f5bfc1ce017bf77a7442f40963a706e608df51))
* **pwd:** fix init pwd ([7d04b8f](https://github.com/eiblog/eiblog/commit/7d04b8f5c0846bcd0c7e07d0fc3704a535f6360a))
### [2.1.2](https://github.com/eiblog/eiblog/compare/v2.1.1...v2.1.2) (2021-07-27)
### [2.1.1](https://github.com/eiblog/eiblog/compare/v2.1.0...v2.1.1) (2021-07-27)
### Bug Fixes
* sqlite error ([e2046d0](https://github.com/eiblog/eiblog/commit/e2046d0d39d9914473fe7b8fae3b18246ed133ce))
### [2.0.6](https://github.com/eiblog/eiblog/compare/v2.0.5...v2.0.6) (2021-05-08)
### Bug Fixes
* workdir loop ([2b277bd](https://github.com/eiblog/eiblog/commit/2b277bd90188d53b90fddd0f6a8edad00f888f53))
* workdir path error ([c18f3b7](https://github.com/eiblog/eiblog/commit/c18f3b7da96e3181b40867a88f9c8cad042d2f44))
## [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
* config path fixed [#1](https://github.com/deepzz0/appdemo/issues/1) ([4343eb4](https://github.com/deepzz0/appdemo/commit/4343eb44e8fffc6825be57393e024c75c4f68b7b))
## 1.0.0 (2020-10-31)

View File

@@ -1,11 +0,0 @@
FROM alpine
MAINTAINER deepzz <deepzz.qi@gmail.com>
RUN apk update
RUN apk add ca-certificates
ADD static/tzdata/Shanghai /etc/localtime
COPY . /eiblog
EXPOSE 9000
WORKDIR /eiblog
ENTRYPOINT ["./eiblog"]

View File

@@ -1,6 +1,6 @@
The MIT License (MIT)
Copyright (c) 2014 Manuel Martínez-Almeida
Copyright (c) 2020-NOW deepzz0 <deepzz.qi@gmail.com>
Permission is hereby granted, free of charge, to any person obtaining a copy
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,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

27
Makefile Normal file
View File

@@ -0,0 +1,27 @@
.PHONY: demo build swag
tag=`git describe --abbrev=0 --tags`
swag:
@scripts/swag_init.sh
_app:
@scripts/new_app.sh
# below you should write
# run eiblog app
eiblog:
@scripts/run_app.sh eiblog
# run backup app
backup:
@scripts/run_app.sh backup
# dist tar
dist:
@scripts/dist_tar.sh $(tag)
# protoc
protoc:
@cd pkg/proto && make protoc

371
README.md
View File

@@ -1,308 +1,109 @@
# EiBlog [![Build Status](https://travis-ci.org/eiblog/eiblog.svg?branch=master)](https://travis-ci.org/eiblog/eiblog)
# EiBlog [![License](https://img.shields.io/badge/license-MIT-brightgreen.svg)](LICENSE.md) [![Versuib](https://img.shields.io/github/tag/eiblog/eiblog.svg)](https://github.com/eiblog/eiblog/releases)
> 系统根据[https://imququ.com](https://imququ.com)一系列文章和方向进行搭建期间获得了QuQu的很大帮助在此表示感谢
用过其它博客系统,不喜欢,不够轻,不够快!这是我开发的第二款博客系统,也实在不想再在这件事情上过多纠结了。`EiBlog` 是一个比较稳定的博客系统,博主已稳定使用多年。这里是 `3.0` 版本,如果仍在使用 `2.0` [请点击这里](https://github.com/eiblog/eiblog/tree/v2)
用过其它博客系统,不喜欢,不够轻,不够快!自己做过共两款博客系统,完美主义的我(毕竟处女座)也实在是不想再在这件事情上过多纠结了。`Eiblog`应该是一个比较稳定的博客系统,且是博主以后使用的博客系统,稳定性和维护你是不用担心的,唯独该系统部署过程太过复杂,并且不推荐没有计算机知识的朋友搭建,欢迎咨询。该博客的个中优点(明显快,安全),等你体验。
但它有着部署简单(上线复杂!)的特点,不推荐没有计算机知识的朋友搭建,欢迎咨询。该博客的个中优点(简洁、轻快,安全),等你体验。
<!--more-->
Docker镜像地址
### 介绍
整个博客系统涉及到模块如下:
* 博客服务:[deepzz0/eiblog](https://hub.docker.com/r/deepzz0/eiblog)
* 博客搜索:[deepzz0/elasticsearch](https://hub.docker.com/r/deepzz0/elasticsearch)
* 数据备份:[deepzz0/backup](https://hub.docker.com/r/deepzz0/backup)
* `MongoDB`,博客采用 mongodb 作为存储数据库。
* `Elasticsearch`,采用`elasticsearch`作为博客的站内搜索,尽管占用内存稍高。
* `Disqus`,作为博客评论系统,国内大部分被墙,故实现两种评论方式。
* `Nginx`,作为反向代理服务器,并做相关`http header`和证书的设置。
* `Google Analytics`,作为博客系统的数据分析统计工具。
* `七牛 CDN`,作为博客系统的静态文件存储,博文的图片附件什么上传至这里。
### 功能变化
相关技术有:
- [x] 增对 **TOTP 双因素认证** 的支持,配置开启后后台绑定
- [x] 新增 **自定义页面**,支持独立页面和内嵌页面
- [x] 优化项目结构,更加清晰,各个 app 之间配置独立
- [ ] 支持多搜索引擎,如数据库原生、[zincsearch](https://github.com/zincsearch/zincsearch)、bleve 等
* `Golang`博客系统后端采用golang编写并开源至[Eiblog](https://github.com/eiblog/eiblog)。
* `HTML Javascript CSS`,博客系统的前端采用`html``jquery`编写,样式采用`CSS`
* `Glide` golang 编写。作为博客系统的包依赖管理器,其开源地址是[Glide](https://github.com/Masterminds/glide)。
* `Docker`,博客系统可 docker 部署,方便,快捷。
* `Docker Compose`,博客系统可完全 docker 运行compose起到很好管理作用。
* `SSL 证书``https`是未来的趋势,整个博客系统都将围绕着`证书`进行,请事先准备好一张有效的 ssl 证书。
* `Travis`作为博客系统的自动构建工具自动构建docker镜像并推送到镜像仓库。
* `Yaml`,博客系统的配置文件使用`yaml`,请悉知。
### 快速体验
作为博主之心血之作,`Eiblog`实现了什么功能,有什么特点,做了什么优化呢?
**二进制**
1. 系统目前只有`首页``专题``归档``友链``关于``搜索`界面。相信已经可以满足大部分用户的需求
2. `.js``.css`等静态文件本地存储,小图片 base64 内置到 css 中,不会产生网络所带来的延迟,加速网页访问。版本控制方式,动态更新静态文件。
3. 采用谷歌统计,并实现异步(将访问信息发给后端,后端提交给谷歌)统计,加速访问速度。
4. 采用直接缓存 markdown 转过的 html 文档的方式,加速后端处理。响应速度均在 3ms 以内,真正极速。
5. 通过 Nginx 的配置,开启压缩缩小传输量,服务器传输证书链、开启`Session Resumption``Session Ticket``OCSP Stapling`等加速证书握手,再次提高速度。
6. 文章评论数量(不重要)后端跑定时脚本,定时更新,所以有时评论数是不对的。这样减少了 api 调用,又再次达到加速访问的目的。
7. 针对`disqus`被墙原因,实现[Jerry Qu](https://imququ.com)的另类评论方式,保证评论的流畅。
8. 开源`Typecho`完整后台系统,全功能`markdown`编辑器,让你体验什么是简洁清爽。
9. 博客后台直接对接`七牛 SDK`,实现后台上传文件和删除文件的简单功能。
10. 采用`elasticsearch`作为站内搜索,添加`google opensearch`功能,搜索更加自然。
1、下载压缩包到 [这里](https://github.com/eiblog/eiblog/releases) 下载 eiblog非backup 相应系统压缩包,然后解压缩
当然,在信息安全方面也没少下功夫,虽然我们只是一个小小的博客系统。
2、启动服务`./backend`
1. `CDN`使用七牛融合CDN`https`化,实现全站`https`。七牛可申请免费证书了。
2. `CT`,证书透明度检测,提供一个开放的审计和监控系统。可以让任何域名所有者或者 CA 确定证书是否被错误签发或者被恶意使用,从而提高 HTTPS 网站的安全性。
3. `OSCP`,在线证书状态协议。用来检验证书合法性的在线查询服务.
4. `HSTS`,强制客户端(如浏览器)使用 HTTPS 与服务器创建连接。可以很好的解决 HTTPS 降级攻击。
5. `HPKP`HTTP公钥固定扩展防范由「伪造或不正当手段获得网站证书」造成的中间人攻击。该功能让我们选择信任哪些`CA`
5. `SSL Protocols`,罗列支持的`TLS`协议SSLv3被证实是不安全的。
6. `SSL dhparam`,迪菲赫尔曼密钥交换。
7. `Cipher suite`,罗列服务器支持加密套件。
可以容易的看到[httpsecurityreport](https://httpsecurityreport.com/?report=deepzz.com)评分`96`[ssllabs](https://www.ssllabs.com/ssltest/analyze.html?d=deepzz.com&latest)评分`A+`,堪称完美。这些安全的相关配置会在后面的部署过程中接触到。
相关图片展示:
![show-home](http://7xokm2.com1.z0.glb.clouddn.com/static/img/show-home1.png)
![show-home2](http://7xokm2.com1.z0.glb.clouddn.com/static/img/show-home2.png)
![show-admin](http://7xokm2.com1.z0.glb.clouddn.com/static/img/show-admin.png)
![show-time](http://7xokm2.com1.z0.glb.clouddn.com/static/img/eiblog-time.png)
> `注`图片1图片2是博客界面图片3是后台界面图片4是性能展示。
好了,说了那么多,吹了那么多,我们实际来动手搭建一个`Eiblog`吧。
### 安装
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
$ go get https://github.com/eiblog/eiblog
```
进行源码编译二进制文件运行。
3、如果你对`docker`技术也有研究的话,你也可以通过`docker`来安装:
``` sh
$ docker pull registry.cn-hangzhou.aliyuncs.com/deepzz/eiblog
**Docker**
```
镜像内部只提供了`eiblog`的二进制文件,因为其它内容定制化的需求过高。所以需要将`conf`、`static`、`views`目录映射出来,后面会具体说到。
### 本地测试
在我们下载好可执行程序之后,我们可以开始本地测试的工作了。
本地测试需要搭建两个服务`mongodb`和`elasticsearch2.4.1`(可选,搜索服务不可用)。
`Eiblog`默认会连接`hostname`为`eidb`和`eisearch`,因此你需要将信息填入`/etc/hosts`下。假如你搭建的`mongodb`地址为`127.0.0.1:27017``elasticsearch`地址为`192.168.99.100:9200`,如:
``` sh
$ sudo vi /etc/hosts
# 在末尾加上两行
127.0.0.1 eidb
192.168.99.100 eisearch
```
#### MongoDB 搭建
1、`MongoDB`搭建Mac 可通过`brew install mongo`进行安装,其它平台请查询资料。
#### Elasticsearch 搭建
2、`Elasticsearch`搭建,它的搭建要些许复杂。博主尚未接触如何直接安装,因此建议通过`docker`搭建。需要注意的是 es 自带的分析器对中文分词是不友好的,这里采用了`elasticsearch-analysis-ik`分词器。如果你想了解更多[Github](https://github.com/medcl/elasticsearch-analysis-ik)或则如何实现[博客站内搜索](https://imququ.com/post/elasticsearch.html)。
* pull 镜像`docker pull elasticsearch:2.4.1`,必需使用该版本。
* 添加环境变量`ES_JAVA_OPTS: "-Xms512m -Xmx512m"`,除非你想让你的服务器爆掉。
* 映射相关目录:
```
conf/es/config:/usr/share/elasticsearch/config
conf/es/plugins:/usr/share/elasticsearch/plugins
conf/es/data:/usr/share/elasticsearch/data
conf/es/logs:/usr/share/elasticsearch/logs
```
 请将这四个目录映射至`eiblog`下的`conf`目录。如果你想查看更多,请查看`docker-compose.yml`文件。
总结一下,`docker`运行 es 的命令为:
``` sh
$ docker run -d --name eisearch \
-p 9200:9200 \
-e ES_JAVA_OPTS: "-Xms512m -Xmx512m" \
-v conf/es/config:/usr/share/elasticsearch/config \
-v conf/es/plugins:/usr/share/elasticsearch/plugins \
-v conf/es/data:/usr/share/elasticsearch/data \
-v conf/es/logs:/usr/share/elasticsearch/logs \
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`的相关部署说明。你如果需要其它方式部署,请参考该方式。
#### 前提准备
这里需要准备一些必要的东西,如果你已准备好。请跳过。
* `一台服务器`。
* `一个域名`,国内服务器需备案。
* `有效的证书`。一般使用免费的就可以。如:`Lets Encrypt`,另外`qcloud`、`七牛`也提供了免费证书的申请,均是全球可信。
* `七牛CDN`。博客只设计接入了七牛cdn相信该CDN服务商不会让你失望。
* `Disqus`。作为博客评论系统,你得有翻墙的能力注册到该账号,具体配置我想又可以写一片博客了。简单说需要`shorname`和`public key`。
* `Google Analystic`。数据统计分析工具。
* `Superfeedr`。加速 RSS 订阅。
* `Twitter`。希望你能够有一个twitter账号。
是不是这么多要求,很费解。其实当初该博客系统只是为个人而设计的,是自己心中想要的那一款。博主些这篇文章不是想要多少人来用该博客,而是希望对那些追求至极的朋友说:你需要这款博客系统。
#### 文件准备
尽管大多数文件已经准备好。但有些默认的文件需要特别指出来,需要你在 CDN 上写特殊的路径。
假如你的 CDN 域名为`st.example.com`,那么:
* `favicon.ico`,其 URL 应该是`st.example.com/static/img/favicon.ico`。故你在 CDN 中的文件名为`static/img/favicon.ico`,以下如是。
* `左侧背景图片``500*1200`左右CDN 中文件名:`static/img/bg04.jpg`。如需更改,请在`eiblog/view/st_blog.css`中替换该名称。
* `头像``160*160~256*256`之间CDN 文件名:`static/img/avatar.jpg`。另外你需要将该图片 `Base64` 编码后替换掉`eiblog/views/st_blog.css`中合适位置的图片。
* `blank.gif`CDN 文件名:`static/img/blank.gif`。该图片请从[这里](https://st.deepzz.com/static/img/blank.gif)下载并上传至你的 CDN。
* `default_avatar.png`CDN 文件名:`static/img/default_avatar.png`,请从[这里](https://st.deepzz.com/static/img/default_avatar.png)下载并上传至你的 CDN。
* `disqus.js`,该文件名是会变的,每次更新如果没有提及就没有改变,更新说明在[这里](https://github.com/eiblog/eiblog/blob/master/CHANGELOG.md)。CDN 文件名格式是:`static/js/name.js`。在我写这篇文章是使用的是:`static/js/disqus_a9d3fd.js`,请从[这里](https://st.deepzz.com/static/js/disqus_a9d3fd.js)下载并上传至你的 CDN。
> `注意`:本人 CDN 做了防盗链处理,故请将这些资源上传至您的 CDN ,以免静态资源不能访问,请悉知。
#### 配置说明
走到这里,我相信只走到`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文件
│   │   └── deepzz.conf
│   ├── ip.blacklist # nginx ip黑名单
│   └── nginx.conf # nginx配置请替换原有配置
├── scts # ct文件
│   ├── aviator.sct
│   └── digicert.sct
├── ssl # 证书文件具体请看deepzz.conf
│   ├── dhparams.pem
│   ├── domain.key
│   ├── domain.pem
│   ├── full_chained.pem
│   └── session_ticket.key
└── tpl # 模版文件
├── feedTpl.xml
├── opensearchTpl.xml
└── sitemapTpl.xml
```
1、app.yml整个程序的配置文件里面已经列出了所有配置项的说明这里不再阐述。
2、blackip.yml如果没有使用`Nginx`,博客内置`ip`过滤系统。
3、`es`全名`elasticsearch`,非常强大的分布式搜索引擎,`github`用的就是它。里面的配置基本不用修改,但`es/analysis/synonym.txt`是同义词,你可以照着已有的随意增加。
```
├── es
│   ├── config
│   │   ├── analysis
│   │   │   └── synonym.txt #同义词配置
│   │   ├── elasticsearch.yml #分词器配置
│   │   ├── logging.yml #日志配置
│   │   └── scripts #脚本
│   └── plugins #中文分词插件
│   └── ik1.10.0
```
> `注意`scripts文件夹虽然是空的但必需存在不然elasticsearch报错。
4、`nginx`,系统采用`nginx`作为代理(相信博客系统也不会独占一台服务器~)。请使用`nginx.conf`替换原`nginx`的配置。博客系统的配置文件是`domain/deepzz.conf`,或则重命名(只要是满足`*.conf`)。`deepzz.conf`文件里面学问是最多的。或许你想一一弄懂,或许…。
> 注意本配置需要更新nginx到最新版openssl更新到1.0.2j,具体请到 Jerry Qu 的[本博客 Nginx 配置之完整篇](https://imququ.com/post/my-nginx-conf.html)查看,了解详情。
5、`scts`,存放 ct 文件。
6、`ssl`,这里存放了所有证书相关的内容。
```
├── dhparams.pem #参见eiblog/conf/nginx/domain/deepzz.conf
├── domain.key #证书私钥,一般颁发者处下载
├── domain.pem #证书链,一般从证书颁发者那可以直接下载到
├── full_chained.pem #参见eiblog/conf/nginx/domain/deepzz.conf
└── session_ticket.key #参见eiblog/conf/nginx/domain/deepzz.conf
```
7、`tpl`模版相关,不用修改。
### 开始部署
#### docker
请确定你已经完成了上面所说的所有步骤,在本地已经测试成功。服务器上`MognoDB`和`Elasticsearch`已经安装并已经运行成功。
首先,请将本地测试好的`conf``static``views`文件夹上传至服务器,建议存储到服务器`/data/eiblog`下。
``` sh
$ tree /data/eiblog -L 1
├── conf
├── static
├── views
```
然后,将镜像 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 \
--link eidb --link eisearch \
$ docker run --name eiblog \
-p 9000:9000 \
-e GODEBUG=netdns=cgo \
-v /data/eiblog/logdata:/eiblog/logdata \
-v /data/eiblog/conf:/eiblog/conf \
-v /data/eiblog/static:/eiblog/static \
-v /data/eiblog/views:/eiblog/views \
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``static``views``docker-compose.yml`文件夹和文件上传至服务器。前三个文件夹建议存储到服务器`/data/eiblog`下,`docker-compose.yml`存放在你使用方便的地方。
> 注意`conf/es/config/scripts`空文件夹是否存在
``` sh
$ tree /data/eiblog -L 1
├── conf
├── static
├── views
$ ls ~/
docker-compose.yml
deepzz0/eiblog:latest
```
然后,执行:
``` sh
$ cd ~
**Compose**
参考项目根目录下的 [docker-compose.yml](https://github.com/eiblog/eiblog/blob/v2/docker-compose.yml),修改相关配置:
```
$ docker-compose up -d
```
等待些许时间,成功运行
然后访问 `localhost:9000` 就可以了,后台地址 `localhost:9000/admin/login`,默认账户密码 `deepzz/deepzz`
> 默认情况下未开启博客搜索 `elasticsearch`,需要的话需要启动 es 服务并修改配置 `app.yml`。
### 成功搭建者博客
**数据库支持**
* [https://razeencheng.com/](https://razeencheng.com/) - Razeen's Blog
| 类型driver | 地址source示例 |
| -------------- | ------------------------------------------------------------ |
| mongodb | mongodb://localhost:27017/eiblog |
| mysql | user:password@tcp(localhost:3306)/eiblog?charset=utf8mb4&parseTime=True&loc=Local |
| postgres | host=localhost port=5432 user=user password=password dbname=eiblog sslmode=disable |
| sqlite | /path/eiblog.db |
| sqlserver | sqlserver://user:password@localhost:9930?database=eiblog |
| clickhouse | tcp://localhost:9000?database=eiblog&username=user&password=password&read_timeout=10&write_timeout=20 |
### 功能特性
本着博客本质用来分享知识的特点,`EiBlog` 不会有较强的定制功能包括主题CDN支持等仅保持常用简单页面与功能
```
首页、专题、归档、友链、关于、搜索
```
功能说明:
* 博客归档,利用时间线帮助我们将归纳博文,内容少于一年按月归档,大于则按年归档。
* 博客专题,有时候博文是同一系列,专题能够帮助我们很好归纳博文,对阅读者是非常友好的。
* 标签系统,每篇博文都可以打上不同标签,使得在归档和专题不满足的情况下自定义归档,这块辅助搜索简直完美。
* 搜索系统依托ElasticSearch实现的站内搜索速度与效率并存再加上google opensearch搜索只流畅。
* 管理后台,内嵌全功能 `Typecho` 后台系统,全功能 `Markdown` 编辑器让你感觉什么是简洁清爽。
* 谷歌统计由于google api的速度问题从而实现了后端API异步统计使得博客页面加载飞速。
* Disqus评论国内评论系统不友好因此选择disqus又由于众所周知原因国内不能用实现另类disqus评论方式。
* 多存储后端支持mongodb、mysql、postgres、sqlite等存储后端。
* 七牛CDN支持在 `Markdown` 编辑器直接上传附件,让你只考虑编辑内容,解放思想。
* 自动备份支持多存储后端的备份功能备份数据保存到七牛CDN上。
当然,为了让整个系统加载速度更快,还做了更多优化措施:
* 文章评论数量(不重要)通过后端跑定时任务获取,所以有时评论数量是不对的,这样减少了 API 调用。
* 整站内容全部内存缓存,`mardown` 文档全部转换为 html 进行缓存,减少了转换过程。
* `.js``.css` 等静态文件浏览器本地存储,小图片 base64 内置到 css 中,二次访问不会产生网络带来的延迟,加速访问。通过版本控制更新。
* 最佳实践 nginx 配置,可以查看 `eiblog.conf`,开启压缩缩小传输量,服务器传输证书链、开启 `Session Resumption``Session Ticket``OCSP Stapling `等加速证书握手,再次提高速度。
### 博客页面
可以容易的看到 [ssllabs](https://www.ssllabs.com/ssltest/analyze.html?d=deepzz.com&latest) 评分`A+`[myssl](https://myssl.com/deepzz.com) 评分`A+`,堪称完美。这些安全的相关配置会在后面的部署过程中接触到。
![show-home](./docs/img/show-home.png)
![show-home2](./docs/img/show-home2.png)
![show-admin](./docs/img/show-admin.png)
### 更多文档
* [安装部署](https://eiblog.github.io/eiblog/install)
* [写作须知](https://eiblog.github.io/eiblog/writing)
* [好玩功能](https://eiblog.github.io/eiblog/amusing)
* [如何备份](https://eiblog.github.io/eiblog/backup)
### 贡献成员
![graphs/contributors](https://opencollective.com/eiblog/contributors.svg?width=890&button=false)
### 授权许可
本项目采用 MIT 开源授权许可证,完整的授权说明已放置在 [LICENSE](https://github.com/eiblog/eiblog/blob/master/LICENSE) 文件中。
如果你的博客使用`Eiblog`搭建,你可以在[这里](https://github.com/eiblog/eiblog/issues/1)提交网址。

463
api.go
View File

@@ -1,463 +0,0 @@
package main
import (
"errors"
"fmt"
"net/http"
"sort"
"strconv"
"strings"
"time"
"github.com/eiblog/eiblog/setting"
"github.com/eiblog/utils/logd"
"github.com/gin-gonic/gin"
"gopkg.in/mgo.v2/bson"
)
const (
NOTICE_SUCCESS = "success"
NOTICE_NOTICE = "notice"
NOTICE_ERROR = "error"
)
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
}
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(bson.M{"$set": bson.M{"email": e, "phonen": pn, "address": ad}})
if err != nil {
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(bson.M{"$set": bson.M{"blogger.blogname": bn, "blogger.btitle": bt, "blogger.beian": ba, "blogger.subtitle": st, "blogger.seriessay": ss, "blogger.archivessay": as}})
if err != nil {
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(bson.M{"$set": bson.M{"password": newPwd}})
if err != nil {
responseNotice(c, NOTICE_NOTICE, err.Error(), "")
return
}
Ei.Password = newPwd
responseNotice(c, NOTICE_SUCCESS, "更新成功", "")
}
func apiPostDelete(c *gin.Context) {
var err error
defer func() {
if err != nil {
logd.Error(err)
responseNotice(c, NOTICE_NOTICE, err.Error(), "")
return
}
responseNotice(c, NOTICE_SUCCESS, "删除成功", "")
}()
err = c.Request.ParseForm()
if err != nil {
return
}
var ids []int32
var i int
for _, v := range c.Request.PostForm["cid[]"] {
i, err = strconv.Atoi(v)
if err != nil || i < 1 {
err = errors.New("参数错误")
return
}
ids = append(ids, int32(i))
}
err = DelArticles(ids...)
if err != nil {
return
}
// elasticsearch 删除索引
err = ElasticDelIndex(ids)
if err != nil {
return
}
}
func apiPostAdd(c *gin.Context) {
var err error
var do string
var 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": // 保存草稿
if err != nil {
responseNotice(c, NOTICE_NOTICE, err.Error(), "")
return
}
c.Redirect(http.StatusFound, "/admin/manage-draft")
case "publish": // 发布
if err != nil {
responseNotice(c, NOTICE_NOTICE, err.Error(), "")
return
}
c.Redirect(http.StatusFound, "/admin/manage-posts")
}
}()
do = c.PostForm("do") // auto or save or publish
slug := c.PostForm("slug")
title := c.PostForm("title")
text := c.PostForm("text")
date := c.PostForm("date")
serie := c.PostForm("serie")
tag := c.PostForm("tags")
update := c.PostForm("update")
if title == "" || text == "" || slug == "" {
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: CheckDate(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 {
ElasticIndex(artc)
DoPings(slug)
}
return
}
artc.ID = int32(cid)
i, a := GetArticle(artc.ID)
if a != nil {
artc.IsDraft = false
artc.Count = a.Count
artc.UpdateTime = a.UpdateTime
Ei.Articles = append(Ei.Articles[0:i], Ei.Articles[i+1:]...)
DelFromLinkedList(a)
ManageTagsArticle(a, false, DELETE)
ManageSeriesArticle(a, false, DELETE)
ManageArchivesArticle(a, false, DELETE)
delete(Ei.MapArticles, a.Slug)
a = nil
}
if CheckBool(update) {
artc.UpdateTime = time.Now()
}
err = UpdateArticle(bson.M{"id": artc.ID}, artc)
if err != nil {
logd.Error(err)
return
}
if !artc.IsDraft {
Ei.MapArticles[artc.Slug] = artc
Ei.Articles = append(Ei.Articles, artc)
sort.Sort(Ei.Articles)
GenerateExcerptAndRender(artc)
// elasticsearch 索引
ElasticIndex(artc)
DoPings(slug)
if artc.ID >= setting.Conf.StartID {
ManageTagsArticle(artc, true, ADD)
ManageSeriesArticle(artc, true, ADD)
ManageArchivesArticle(artc, true, ADD)
AddToLinkedList(artc.ID)
}
}
}
func apiSerieDelete(c *gin.Context) {
err := c.Request.ParseForm()
if err != nil {
responseNotice(c, NOTICE_NOTICE, err.Error(), "")
return
}
// 只能逐一删除
for _, v := range c.Request.PostForm["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, "删除成功", "")
}
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, "操作成功", "")
}
// 暂未启用
func apiSerieSort(c *gin.Context) {
err := c.Request.ParseForm()
if err != nil {
responseNotice(c, NOTICE_NOTICE, err.Error(), "")
return
}
v := c.Request.PostForm["mid[]"]
logd.Debug(v)
}
func apiDraftDelete(c *gin.Context) {
err := c.Request.ParseForm()
if err != nil {
responseNotice(c, NOTICE_NOTICE, err.Error(), "")
return
}
for _, v := range c.Request.PostForm["mid[]"] {
i, err := strconv.Atoi(v)
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) {
logd.Debug(c.PostForm("key"))
logd.Debug(c.Request.PostForm)
err := c.Request.ParseForm()
if err != nil {
responseNotice(c, NOTICE_NOTICE, err.Error(), "")
return
}
for _, v := range c.Request.PostForm["mid[]"] {
i, err := strconv.Atoi(v)
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) {
logd.Debug(c.PostForm("key"))
logd.Debug(c.Request.PostForm)
err := c.Request.ParseForm()
if err != nil {
responseNotice(c, NOTICE_NOTICE, err.Error(), "")
return
}
for _, v := range c.Request.PostForm["mid[]"] {
i, err := strconv.Atoi(v)
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, "恢复成功", "")
}
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 := c.Request.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),
})
}
func apiFileDelete(c *gin.Context) {
var err error
defer func() {
if err != nil {
logd.Error(err)
}
c.String(http.StatusOK, "删掉了吗?鬼知道。。。")
}()
name := c.PostForm("title")
if name == "" {
err = errors.New("参数错误")
return
}
err = FileDelete(name)
}
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
View File

@@ -0,0 +1 @@
Other assets to go along with your repository (images, logos, etc).

View File

Before

Width:  |  Height:  |  Size: 76 KiB

After

Width:  |  Height:  |  Size: 76 KiB

View File

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 14 KiB

View File

Before

Width:  |  Height:  |  Size: 37 KiB

After

Width:  |  Height:  |  Size: 37 KiB

View File

Before

Width:  |  Height:  |  Size: 63 KiB

After

Width:  |  Height:  |  Size: 63 KiB

View File

Before

Width:  |  Height:  |  Size: 44 KiB

After

Width:  |  Height:  |  Size: 44 KiB

View File

Before

Width:  |  Height:  |  Size: 92 KiB

After

Width:  |  Height:  |  Size: 92 KiB

View File

Before

Width:  |  Height:  |  Size: 22 KiB

After

Width:  |  Height:  |  Size: 22 KiB

299
back.go
View File

@@ -1,299 +0,0 @@
// Package main provides ...
package main
import (
"bytes"
"fmt"
"html/template"
"net/http"
"strconv"
"time"
"github.com/eiblog/eiblog/setting"
"github.com/eiblog/utils/logd"
"github.com/gin-gonic/contrib/sessions"
"github.com/gin-gonic/gin"
"gopkg.in/mgo.v2/bson"
)
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", 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(bson.M{"$set": bson.M{"loginip": Ei.LoginIP, "logintime": Ei.LoginTime}})
c.Redirect(http.StatusFound, "/admin/profile")
}
func GetBack() gin.H {
return gin.H{"Author": Ei.Username, "Kodo": setting.Conf.Kodo}
}
// 个人配置
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})
}
h["Tags"] = tags
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.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)
}
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
View 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).

View File

@@ -0,0 +1,23 @@
FROM golang:1.23 AS builder
WORKDIR /eiblog
COPY . .
RUN scripts/run_build.sh backup
FROM alpine:latest
LABEL maintainer="deepzz.qi@gmail.com"
RUN apk add --update --no-cache tzdata ca-certificates \
mongodb-tools libc6-compat gcompat
COPY README.md /app/README.md
COPY CHANGELOG.md /app/CHANGELOG.md
COPY LICENSE /app/LICENSE
COPY --from=builder /eiblog/cmd/backup/backend /app/backend
COPY cmd/backup/etc /app/etc
EXPOSE 9001
WORKDIR /app
CMD ["./backend"]

View File

@@ -0,0 +1,22 @@
FROM golang:1.23 AS builder
WORKDIR /eiblog
COPY . .
RUN scripts/run_build.sh eiblog
FROM alpine:latest
LABEL maintainer="deepzz.qi@gmail.com"
RUN apk add --update --no-cache tzdata ca-certificates
COPY README.md /app/README.md
COPY CHANGELOG.md /app/CHANGELOG.md
COPY LICENSE /app/LICENSE
COPY --from=builder /eiblog/cmd/eiblog/backend /app/backend
COPY cmd/eiblog/etc /app/etc
EXPOSE 9000
WORKDIR /app
CMD ["./backend"]

View File

@@ -1,10 +0,0 @@
#!/bin/bash
echo "go build..."
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build && \
domain="registry.cn-hangzhou.aliyuncs.com" && \
docker build -t $domain/deepzz/eiblog . && \
read -p "是否上传到服务器(y/n):" word && \
if [ $word = "y" ] ;then
docker push $domain/deepzz/eiblog
fi

View File

@@ -1,44 +0,0 @@
package main
import (
"regexp"
"strconv"
"time"
)
func CheckEmail(e string) bool {
reg := regexp.MustCompile(`^(\w)+([\.\-]\w+)*@(\w)+((\.\w+)+)$`)
return reg.MatchString(e)
}
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)
}
func CheckSMS(sms string) bool {
reg := regexp.MustCompile(`^\+\d+$`)
return reg.MatchString(sms)
}
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()
}
func CheckSerieID(sid string) int32 {
if id, err := strconv.Atoi(sid); err == nil {
return int32(id)
}
return 0
}
func CheckBool(str string) bool {
return str == "true" || str == "1"
}

View File

@@ -1,29 +0,0 @@
package main
import (
"testing"
)
func TestCheckEmail(t *testing.T) {
e := "xx@email.com"
e1 := "xxxxemail.com"
e2 := "xxx#email.com"
t.Log(CheckEmail(e))
t.Log(CheckEmail(e1))
t.Log(CheckEmail(e2))
}
func TestCheckDomain(t *testing.T) {
d := "123.com"
d1 := "http://123.com"
d2 := "https://123.com"
d3 := "123#.com"
d4 := "123.coooom"
t.Log(CheckDomain(d))
t.Log(CheckDomain(d1))
t.Log(CheckDomain(d2))
t.Log(CheckDomain(d3))
t.Log(CheckDomain(d4))
}

7
cmd/README.md Normal file
View 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.

View File

@@ -0,0 +1,70 @@
package config
import (
"os"
"path/filepath"
"strings"
"github.com/eiblog/eiblog/pkg/config"
"github.com/sirupsen/logrus"
"gopkg.in/yaml.v3"
)
// Config config
type Config struct {
config.APIMode
Database config.Database // 数据库配置
BackupTo string // 备份到, default: qiniu
Interval string // 备份周期, default: 7d
Validity int // 备份保留时间, default: 60
Qiniu config.Qiniu // 七牛OSS配置
}
// Conf 配置
var Conf Config
// load config file
func init() {
// run mode
mode := config.RunModeProd
if m := os.Getenv("RUN_MODE"); m != "" {
mode = config.RunMode(m)
if !mode.IsRunMode() {
panic("config: unsupported env RUN_MODE: " + mode)
}
}
logrus.Infof("Run mode:%s", mode)
// 加载配置文件
etc, err := config.WorkEtcPath()
if err != nil {
panic(err)
}
path := filepath.Join(etc, "app.yml")
data, err := os.ReadFile(path)
if err != nil {
panic(err)
}
err = yaml.Unmarshal(data, &Conf)
if err != nil {
panic(err)
}
Conf.RunMode = mode
// read env
readDatabaseEnv()
}
func readDatabaseEnv() {
key := strings.ToUpper(Conf.Name) + "_DB_DRIVER"
if d := os.Getenv(key); d != "" {
Conf.Database.Driver = d
}
key = strings.ToUpper(Conf.Name) + "_DB_SOURCE"
if s := os.Getenv(key); s != "" {
Conf.Database.Source = s
}
}

60
cmd/backup/docs/docs.go Normal file
View File

@@ -0,0 +1,60 @@
// Package docs Code generated by swaggo/swag. DO NOT EDIT
package docs
import "github.com/swaggo/swag"
const docTemplate = `{
"schemes": {{ marshal .Schemes }},
"swagger": "2.0",
"info": {
"description": "{{escape .Description}}",
"title": "{{.Title}}",
"contact": {},
"version": "{{.Version}}"
},
"host": "{{.Host}}",
"basePath": "{{.BasePath}}",
"paths": {
"/ping": {
"get": {
"description": "ping",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"ping"
],
"summary": "ping",
"responses": {
"200": {
"description": "it's ok",
"schema": {
"type": "string"
}
}
}
}
}
}
}`
// SwaggerInfo holds exported Swagger Info so clients can modify it
var SwaggerInfo = &swag.Spec{
Version: "1.0",
Host: "",
BasePath: "",
Schemes: []string{},
Title: "backup API",
Description: "This is a backup server.",
InfoInstanceName: "swagger",
SwaggerTemplate: docTemplate,
LeftDelim: "{{",
RightDelim: "}}",
}
func init() {
swag.Register(SwaggerInfo.InstanceName(), SwaggerInfo)
}

View File

@@ -0,0 +1,34 @@
{
"swagger": "2.0",
"info": {
"description": "This is a backup server.",
"title": "backup API",
"contact": {},
"version": "1.0"
},
"paths": {
"/ping": {
"get": {
"description": "ping",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"ping"
],
"summary": "ping",
"responses": {
"200": {
"description": "it's ok",
"schema": {
"type": "string"
}
}
}
}
}
}
}

View File

@@ -0,0 +1,22 @@
info:
contact: {}
description: This is a backup server.
title: backup API
version: "1.0"
paths:
/ping:
get:
consumes:
- application/json
description: ping
produces:
- application/json
responses:
"200":
description: it's ok
schema:
type: string
summary: ping
tags:
- ping
swagger: "2.0"

14
cmd/backup/etc/app.yml Normal file
View File

@@ -0,0 +1,14 @@
apimode:
name: cmd-backup
listen: 0.0.0.0:9000
database: # 数据库配置
driver: mongodb
source: mongodb://localhost:27017/eiblog
backupto: qiniu # 备份到, default: qiniu
interval: 7d # 备份周期, default: 7d
validity: 60 # 备份保留时间, default: 60
qiniu: # 七牛OSS
bucket: eiblog
domain: st.deepzz.com
accesskey: MB6AXl_Sj_mmFsL-Lt59Dml2Vmy2o8XMmiCbbSeC
secretkey: BIrMy0fsZ0_SHNceNXk3eDuo7WmVYzj2-zrmd5Tf

View File

@@ -0,0 +1,17 @@
package internal
import (
"github.com/eiblog/eiblog/cmd/backup/config"
"github.com/eiblog/eiblog/pkg/third/qiniu"
)
// QiniuClient 七牛客户端
var QiniuClient *qiniu.QiniuClient
func init() {
var err error
QiniuClient, err = qiniu.NewQiniuClient(config.Conf.Qiniu)
if err != nil {
panic(err)
}
}

View File

@@ -0,0 +1,24 @@
package ping
import (
"net/http"
"github.com/gin-gonic/gin"
)
// RegisterRoutes register routes
func RegisterRoutes(group gin.IRoutes) {
group.GET("/ping", handlePing)
}
// handlePing ping
// @Summary ping
// @Description ping
// @Tags ping
// @Accept json
// @Produce json
// @Success 200 {string} string "it's ok"
// @Router /ping [get]
func handlePing(c *gin.Context) {
c.String(http.StatusOK, "it's ok")
}

View File

@@ -0,0 +1,14 @@
package swag
import (
_ "github.com/eiblog/eiblog/cmd/eiblog/docs" // docs
"github.com/gin-gonic/gin"
swaggerFiles "github.com/swaggo/files"
ginSwagger "github.com/swaggo/gin-swagger"
)
// RegisterRoutes register routes
func RegisterRoutes(group gin.IRoutes) {
group.GET("/swagger/*any", ginSwagger.WrapHandler(swaggerFiles.Handler))
}

View File

@@ -0,0 +1,7 @@
package db
// Storage 备份恢复器
type Storage interface {
Backup(name string) (string, error)
Restore(path string) error
}

View File

@@ -0,0 +1,75 @@
package db
import (
"context"
"fmt"
"net/url"
"os/exec"
"time"
"github.com/eiblog/eiblog/cmd/backup/config"
pdb "github.com/eiblog/eiblog/pkg/connector/db"
)
// MongoStorage 备份恢复器
type MongoStorage struct{}
// Backup 备份
func (r MongoStorage) Backup(name string) (string, error) {
ctx, cancel := context.WithTimeout(context.Background(), time.Second*20)
defer cancel()
// dump
u, err := url.Parse(config.Conf.Database.Source)
if err != nil {
return "", err
}
if u.Path == "" {
return "", fmt.Errorf("no database specified")
}
arg := fmt.Sprintf("mongodump -h %s -d %s -o /tmp", u.Host, u.Path)
cmd := exec.CommandContext(ctx, "sh", "-c", arg)
err = cmd.Run()
if err != nil {
return "", err
}
// tar
arg = fmt.Sprintf("tar czf /tmp/%s -C /tmp %s", name, u.Path)
cmd = exec.CommandContext(ctx, "sh", "-c", arg)
err = cmd.Run()
if err != nil {
return "", err
}
return "/tmp/" + name, nil
}
// Restore 恢复
func (r MongoStorage) Restore(path string) error {
ctx, cancel := context.WithTimeout(context.Background(), time.Second*20)
defer cancel()
// drop database
database, err := pdb.NewMDB(ctx, config.Conf.Database)
if err != nil {
return err
}
err = database.Drop(ctx)
if err != nil {
return err
}
// unarchive
arg := fmt.Sprintf("tar xzf %s -C /tmp", path)
cmd := exec.CommandContext(ctx, "sh", "-c", arg)
err = cmd.Run()
if err != nil {
return err
}
// restore
u, err := url.Parse(config.Conf.Database.Source)
if err != nil {
return err
}
arg = fmt.Sprintf("mongorestore -h %s -d %s /tmp/%s", u.Host, u.Path, u.Path)
cmd = exec.CommandContext(ctx, "sh", "-c", arg)
return cmd.Run()
}

View File

@@ -0,0 +1,95 @@
package timer
import (
"errors"
"fmt"
"strconv"
"time"
"github.com/eiblog/eiblog/cmd/backup/config"
"github.com/eiblog/eiblog/cmd/backup/handler/timer/db"
"github.com/eiblog/eiblog/cmd/backup/handler/timer/to"
"github.com/sirupsen/logrus"
)
// Start to backup with ticker
func Start(restore bool) (err error) {
var (
storage db.Storage
backupTo to.BackupRestorer
)
// backup from
switch config.Conf.Database.Driver {
case "mongodb":
storage = db.MongoStorage{}
default:
return errors.New("timer: unknown backup from driver: " +
config.Conf.Database.Driver)
}
// backup to
switch config.Conf.BackupTo {
case "qiniu":
backupTo = to.QiniuBackupRestorer{}
default:
return errors.New("timer: unknown backup to driver: " +
config.Conf.BackupTo)
}
// restore
if restore {
path, err := backupTo.Download()
if err != nil {
return err
}
err = storage.Restore(path)
if err != nil {
return err
}
logrus.Info("timer: Restore success")
}
// backup
interval, err := ParseDuration(config.Conf.Interval)
if err != nil {
return err
}
t := time.NewTicker(interval)
for now := range t.C {
name := fmt.Sprintf("eiblog-%s.tar.gz", now.Format("2006-01-02"))
path, err := storage.Backup(name)
if err != nil {
logrus.Error("timer: Start.Backup: ", now.Format(time.RFC3339), err)
continue
}
err = backupTo.Upload(path)
if err != nil {
logrus.Error("timer: Start.Backup: ", now.Format(time.RFC3339), err)
}
}
return nil
}
// ParseDuration parse string to duration
func ParseDuration(d string) (time.Duration, error) {
if len(d) == 0 {
return 0, errors.New("timer: incorrect duration input")
}
length := len(d)
switch d[length-1] {
case 's', 'm', 'h':
return time.ParseDuration(d)
case 'd':
di, err := strconv.Atoi(d[:length-1])
if err != nil {
return 0, err
}
return time.Duration(di) * time.Hour * 24, nil
}
return 0, errors.New("timer: unsupported duration:" + d)
}

View File

@@ -0,0 +1,66 @@
package to
import (
"os"
"path/filepath"
"github.com/eiblog/eiblog/cmd/backup/config"
"github.com/eiblog/eiblog/cmd/backup/handler/internal"
"github.com/eiblog/eiblog/pkg/third/qiniu"
)
// QiniuBackupRestorer qiniu backup restorer
type QiniuBackupRestorer struct{}
// Upload implements timer.BackupRestorer
func (s QiniuBackupRestorer) Upload(path string) error {
name := filepath.Base(path)
// upload file
f, err := os.Open(path)
if err != nil {
return err
}
fi, err := f.Stat()
if err != nil {
return err
}
uploadParams := qiniu.UploadParams{
Name: filepath.Join("blog", name), // blog/eiblog-xx.tar.gz
Size: fi.Size(),
Data: f,
NoCompletePath: true,
}
_, err = internal.QiniuClient.Upload(uploadParams)
if err != nil {
return err
}
// after days delete
deleteParams := qiniu.DeleteParams{
Name: filepath.Join("blog", name), // blog/eiblog-xx.tar.gz
Days: config.Conf.Validity,
NoCompletePath: true,
}
return internal.QiniuClient.Delete(deleteParams)
}
// Download implements timer.BackupRestorer
func (s QiniuBackupRestorer) Download() (string, error) {
// backup file
params := qiniu.ContentParams{
Prefix: "blog/",
}
raw, err := internal.QiniuClient.Content(params)
if err != nil {
return "", err
}
path := filepath.Join("/tmp", "eiblog.tar.gz")
f, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE, 0644)
if err != nil {
return "", err
}
_, _ = f.Write(raw)
defer f.Close()
return path, nil
}

View File

@@ -0,0 +1,7 @@
package to
// BackupRestorer 备份存储接口
type BackupRestorer interface {
Upload(path string) error
Download() (path string, err error)
}

61
cmd/backup/main.go Normal file
View File

@@ -0,0 +1,61 @@
// Package main provides ...
package main
import (
"flag"
"github.com/eiblog/eiblog/cmd/backup/config"
"github.com/eiblog/eiblog/cmd/backup/handler/ping"
"github.com/eiblog/eiblog/cmd/backup/handler/swag"
"github.com/eiblog/eiblog/cmd/backup/handler/timer"
"github.com/gin-gonic/gin"
"github.com/sirupsen/logrus"
)
// @title backup API
// @version 1.0
// @description This is a backup server.
var restore bool
func init() {
flag.BoolVar(&restore, "restore", false, "restore data into mongodb")
}
func main() {
logrus.Info("Hi, it's App " + config.Conf.Name)
flag.Parse()
endRun := make(chan error, 1)
runCommand(restore, endRun)
runHTTPServer(endRun)
logrus.Fatal(<-endRun)
}
func runCommand(restore bool, endRun chan error) {
go func() {
endRun <- timer.Start(restore)
}()
}
func runHTTPServer(endRun chan error) {
if config.Conf.RunMode.IsReleaseMode() {
gin.SetMode(gin.ReleaseMode)
}
e := gin.Default()
// swag
swag.RegisterRoutes(e)
// route
ping.RegisterRoutes(e)
// start
go func() {
endRun <- e.Run(config.Conf.Listen)
}()
logrus.Info("HTTP server running on: " + config.Conf.Listen)
}

View File

@@ -0,0 +1,86 @@
package config
import (
"os"
"path/filepath"
"strings"
"github.com/eiblog/eiblog/pkg/config"
"github.com/sirupsen/logrus"
"gopkg.in/yaml.v3"
)
var (
// Conf 配置
Conf Config
// EtcDir 工作目录
EtcDir string
)
// Config config
type Config struct {
config.APIMode
// 静态资源版本, 每次更改了 js/css 都需要提高该值
StaticVersion int
// 数据库配置
Database config.Database
// 热词, 手动指定, 用于搜索
HotWords []string
// Elasticsearch 配置
ESHost string
General config.General
Disqus config.Disqus
Google config.Google
Qiniu config.Qiniu
Twitter config.Twitter
FeedRPC config.FeedRPC
Account config.Account
Pages []config.CustomPage
}
// init 初始化配置
func init() {
// run mode
mode := config.RunModeProd
if rm := os.Getenv("RUN_MODE"); rm != "" {
mode = config.RunMode(rm)
if !mode.IsRunMode() {
panic("config: unsupported env RUN_MODE: " + mode)
}
}
logrus.Infof("Run mode:%s", mode)
// 加载配置文件
var err error
EtcDir, err = config.WorkEtcPath()
if err != nil {
panic(err)
}
path := filepath.Join(EtcDir, "app.yml")
data, err := os.ReadFile(path)
if err != nil {
panic(err)
}
err = yaml.Unmarshal(data, &Conf)
if err != nil {
panic(err)
}
Conf.RunMode = mode
// 读取环境变量配置
readDatabaseEnv()
}
func readDatabaseEnv() {
key := strings.ToUpper(Conf.Name) + "_DB_DRIVER"
if d := os.Getenv(key); d != "" {
Conf.Database.Driver = d
}
key = strings.ToUpper(Conf.Name) + "_DB_SOURCE"
if s := os.Getenv(key); s != "" {
Conf.Database.Source = s
}
}

36
cmd/eiblog/docs/docs.go Normal file
View File

@@ -0,0 +1,36 @@
// Package docs Code generated by swaggo/swag. DO NOT EDIT
package docs
import "github.com/swaggo/swag"
const docTemplate = `{
"schemes": {{ marshal .Schemes }},
"swagger": "2.0",
"info": {
"description": "{{escape .Description}}",
"title": "{{.Title}}",
"contact": {},
"version": "{{.Version}}"
},
"host": "{{.Host}}",
"basePath": "{{.BasePath}}",
"paths": {}
}`
// SwaggerInfo holds exported Swagger Info so clients can modify it
var SwaggerInfo = &swag.Spec{
Version: "",
Host: "",
BasePath: "",
Schemes: []string{},
Title: "",
Description: "",
InfoInstanceName: "swagger",
SwaggerTemplate: docTemplate,
LeftDelim: "{{",
RightDelim: "}}",
}
func init() {
swag.Register(SwaggerInfo.InstanceName(), SwaggerInfo)
}

View File

@@ -0,0 +1,7 @@
{
"swagger": "2.0",
"info": {
"contact": {}
},
"paths": {}
}

View File

@@ -0,0 +1,4 @@
info:
contact: {}
paths: {}
swagger: "2.0"

59
cmd/eiblog/etc/app.yml Normal file
View File

@@ -0,0 +1,59 @@
apimode:
name: cmd-eiblog
listen: 0.0.0.0:9000
host: example.com
database: # 数据库配置
driver: sqlite
source: ./db.sqlite
hotwords: # 热搜词
- docker
- mongodb
- curl
- dns
staticversion: 1
eshost: # http://elasticsearch:9200
pages:
- name: 自定义页面
path: /page/custom.html
showinnav: true
isembed: false
- name: 自定义页面2
path: /page/custom2.html
showinnav: true
isembed: true
general: # 常规配置
pagenum: 10 # 首页展示文章数量
pagesize: 20 # 管理界面
descprefix: "Desc:" # 文章描述前缀
identifier: <!--more--> # 截取预览标识
length: 400 # 自动截取预览, 字符数
timezone: Asia/Shanghai # 时区
twofactor: true # 是否启用两步验证
disqus: # 评论相关
shortname: xxxxxx
publickey: wdSgxRm9rdGAlLKFcFdToBe3GT4SibmV7Y8EjJQ0r4GWXeKtxpopMAeIeoI2dTEg
accesstoken: 50023908f39f4607957e909b495326af
google: # 谷歌分析
url: https://www.google-analytics.com/g/collect
tid: G-xxxxxxxxxx
v: "2"
adsense: <script async src="https://pagead2.googlesyndication.com/xxx" crossorigin="anonymous"></script>
qiniu: # 七牛OSS
bucket: eiblog
domain: st.deepzz.com
accesskey: MB6AXl_Sj_mmFsL-Lt59Dml2Vmy2o8XMmiCbbSeC
secretkey: BIrMy0fsZ0_SHNceNXk3eDuo7WmVYzj2-zrmd5Tf
twitter: # twitter card
card: summary
site: deepzz02
image: st.deepzz.com/static/img/avatar.jpg
address: twitter.com/deepzz02
feedrpc: # rss ping
feedrurl: https://deepzz.superfeedr.com/
pingrpc:
- http://ping.baidu.com/ping/RPC2
- http://rpc.pingomatic.com/
# 数据初始化操作,可到博客后台修改
account:
username: deepzz # *后台登录用户名
password: deepzz # *登录明文密码

View File

Before

Width:  |  Height:  |  Size: 847 B

After

Width:  |  Height:  |  Size: 847 B

View File

Before

Width:  |  Height:  |  Size: 2.5 KiB

After

Width:  |  Height:  |  Size: 2.5 KiB

View File

Before

Width:  |  Height:  |  Size: 6.3 KiB

After

Width:  |  Height:  |  Size: 6.3 KiB

View File

Before

Width:  |  Height:  |  Size: 4.7 KiB

After

Width:  |  Height:  |  Size: 4.7 KiB

View File

Before

Width:  |  Height:  |  Size: 2.0 KiB

After

Width:  |  Height:  |  Size: 2.0 KiB

View File

Before

Width:  |  Height:  |  Size: 1.2 KiB

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

Before

Width:  |  Height:  |  Size: 4.2 KiB

After

Width:  |  Height:  |  Size: 4.2 KiB

View File

@@ -0,0 +1,8 @@
<html>
<head>
<title>Custom Page</title>
</head>
<body>
<h1>Custom Page</h1>
</body>
</html>

View File

@@ -0,0 +1,10 @@
{{define "custom2.html"}}
<div id=content class=inner>
<article class="post" itemscope itemtype="http://schema.org/Article">
<h1 class=title itemprop=headline>Custom Page</h1>
<div class="entry-content" itemprop=articleBody>
<p>Custom Page</p>
</div>
</article>
</div>
{{end}}

View File

@@ -0,0 +1 @@
This is the place to put your project's website data if you are not using GitHub pages.

View File

@@ -60,7 +60,7 @@
</ul>
</nav>
<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 class="main">

View File

@@ -50,8 +50,8 @@
<td><a href="/admin/write-post?cid={{.ID}}">{{.Title}}</a></td>
<td>{{.Author}}</td>
<td>{{if gt .SerieID 0}}专题ID:{{.SerieID}}{{else}}--{{end}}</td>
<td>{{dateformat .CreateTime "2006/01/02 15:04"}}</td>
<td>{{dateformat .UpdateTime "2006/01/02 15:04"}}</td>
<td>{{dateformat .CreatedAt "2006/01/02 15:04"}}</td>
<td>{{dateformat .UpdatedAt "2006/01/02 15:04"}}</td>
</tr>
{{end}}
</tbody>

View File

@@ -22,10 +22,10 @@
<label for=password class="sr-only">密码</label>
<input type=password id=password name=password class="text-l w-100" placeholder="密码">
</p>
<!-- <p>
{{if .TwoFactor}}<p>
<label for=code class="sr-only">两步验证</label>
<input type=text id=code name=code class="text-l w-100" placeholder="两步验证">
</p> -->
</p>{{end}}
<p class=submit>
<button type=submit class="btn btn-l w-100 primary">登录</button>
</p>

View File

@@ -35,7 +35,7 @@
<section class="typecho-post-option" role="application">
<label for="date" class="typecho-label">发布日期</label>
<p>
<input class="typecho-date w-100" type="text" name="date" id="date" {{with .Edit}}value="{{dateformat .CreateTime "2006-01-02 15:04"}}"{{end}} />
<input class="typecho-date w-100" type="text" name="date" id="date" {{with .Edit}}value="{{dateformat .CreatedAt "2006-01-02 15:04"}}"{{end}} />
</p>
</section>
<section class="typecho-post-option category-option">
@@ -265,7 +265,6 @@ $(document).ready(function() {
function autoSaveListener() {
setInterval(function() {
idInput.val(cid);
var data = form.serialize();
if (savedData != data && !locked) {
@@ -275,7 +274,7 @@ $(document).ready(function() {
$.post(formAction, data + '&do=auto', function(o) {
savedData = data;
lastSaveTime = o.time;
cid = o.cid;
idInput.val(o.cid);
autoSave.text('已保存' + ' (' + o.time + ')').effect('highlight', 1000);
locked = false;
}, 'json');

View File

@@ -64,12 +64,12 @@
<td><a href="/post/{{.Slug}}.html#comments" class="balloon-button size-1">{{.Count}}</a></td>
<td>
<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>{{.Author}}</td>
<td>{{if gt .SerieID 0}}专题ID:{{.SerieID}}{{else}}--{{end}}</td>
<td>{{dateformat .CreateTime "06/01/02 15:04"}}</td>
<td>{{dateformat .UpdateTime "06/01/02 15:04"}}</td>
<td>{{dateformat .CreatedAt "06/01/02 15:04"}}</td>
<td>{{dateformat .UpdatedAt "06/01/02 15:04"}}</td>
</tr>
{{end}}
</tbody>

View File

@@ -3,15 +3,41 @@
<div class="typecho-page-title">
<h2>个人设置</h2>
</div>
{{with .Account}}
{{with .Ei}}
<div class="row typecho-page-main">
<div class="col-mb-12 col-tb-3">
<p>
<img class="profile-avatar" src="//{{$.Kodo.Domain}}/static/img/avatar.jpg" alt="{{.BlogName}}" />
<img class="profile-avatar" src="//{{$.Qiniu.Domain}}/static/img/avatar.png" alt="{{.Blogger.BlogName}}" />
</p>
<h2>{{.BlogName}}</h2>
<p>{{.SubTitle}}</p>
<p>最后登录: {{dateformat .LoginTime "2006/01/02 15:04"}}</p>
<h2>{{.Blogger.BlogName}}</h2>
<p>{{.Blogger.SubTitle}}</p>
<p>最后登录: {{dateformat .Account.LoginAt "2006/01/02 15:04"}}</p>
{{if .Account.TwoFactorSecret}}
<strong>2FA 已绑定 <a class="unbind-2fa" href="/admin/profile?unbind=true">解绑</a></strong>
{{end}}
{{if $.TwoFactorSecret}}
<strong>2FA 绑定</strong>
<form action="/admin/api/twofactor" method="post" enctype="application/x-www-form-urlencoded">
<ul class="typecho-option">
<li>
<img src="{{$.TwoFactorSecret}}" alt="2fa code" style="width:180px;height:180px;">
</li>
</ul>
<ul class="typecho-option">
<li>
<input id="2fa-0-1" name="code" type="text" class="text" />
<p class="description">
输入 2FA 验证码, 用于确认绑定.</p>
</li>
</ul>
<ul class="typecho-option typecho-option-submit">
<li>
<button type="submit" class="btn primary">
确认绑定</button>
</li>
</ul>
</form>
{{end}}
</div>
<div class="col-mb-12 col-tb-6 col-tb-offset-1 typecho-content-panel" role="form">
<section>
@@ -21,7 +47,7 @@
<li>
<label class="typecho-label" for="info-0-1">
个人邮箱</label>
<input id="info-0-1" name="email" type="text" class="text" value="{{.Email}}" />
<input id="info-0-1" name="email" type="text" class="text" value="{{.Account.Email}}" />
<p class="description">
用于发送告警邮件及其它通知, 建议填写, 如: example@163.com.</p>
</li>
@@ -30,7 +56,7 @@
<li>
<label class="typecho-label" for="info-0-2">
移动电话</label>
<input id="info-0-2" name="phoneNumber" type="text" class="text" value="{{.PhoneN}}" />
<input id="info-0-2" name="phoneNumber" type="text" class="text" value="{{.Account.PhoneN}}" />
<p class="description">
选择填写, 如: 8615123456789.</p>
</li>
@@ -39,7 +65,7 @@
<li>
<label class="typecho-label" for="info-0-3">
家庭住址</label>
<input id="info-0-3" name="address" type="text" class="text" value="{{.Address}}" />
<input id="info-0-3" name="address" type="text" class="text" value="{{.Account.Address}}" />
<p class="description">
选择填写, 如: xx省xx市xx区(县)xxxx小区xxx号.</p>
</li>
@@ -60,7 +86,7 @@
<li>
<label class="typecho-label" for="blog-0-1">
博客昵称 *</label>
<input id="blog-0-1" name="blogName" type="text" class="text" value="{{.BlogName}}" />
<input id="blog-0-1" name="blogName" type="text" class="text" value="{{.Blogger.BlogName}}" />
<p class="description">
用户昵称可以与用户名不同, 用于前台显示.
<br />如果你将此项留空, 将默认使用登录用户名.</p>
@@ -68,45 +94,45 @@
</ul>
<ul class="typecho-option">
<li>
<label class="typecho-label" for="blog-0-4">
<label class="typecho-label" for="blog-0-2">
标题显示 *</label>
<input id="blog-0-4" name="bTitle" type="text" class="text" value="{{.BTitle}}" />
<input id="blog-0-2" name="bTitle" type="text" class="text" value="{{.Blogger.BTitle}}" />
<p class="description">
用于所有页面的title组成, 如: Deepzz's Blog</p>
</li>
</ul>
<ul class="typecho-option">
<li>
<label class="typecho-label" for="blog-0-2">
<label class="typecho-label" for="blog-0-3">
个人格言</label>
<input id="blog-0-2" name="subTitle" type="text" class="text" value="{{.SubTitle}}" />
<input id="blog-0-3" name="subTitle" type="text" class="text" value="{{.Blogger.SubTitle}}" />
<p class="description">
简介或格言, 如: 生活百般滋味, 人生需要笑对.</p>
</li>
</ul>
<ul class="typecho-option">
<li>
<label class="typecho-label" for="blog-0-3">
<label class="typecho-label" for="blog-0-4">
备案号</label>
<input id="blog-0-3" name="beiAn" type="text" class="text" value="{{.BeiAn}}" />
<input id="blog-0-4" name="beiAn" type="text" class="text" value="{{.Blogger.BeiAn}}" />
<p class="description">
用于底部显示, 不添加则不显示, 如: 蜀 ICP 备 16021362 号</p>
</li>
</ul>
<ul class="typecho-option">
<li>
<label class="typecho-label" for="blog-0-4">
<label class="typecho-label" for="blog-0-5">
专题前说</label>
<textarea id="blog-0-4" name="seriessay">{{.SeriesSay}}</textarea>
<textarea id="blog-0-5" name="seriessay">{{.Blogger.SeriesSay}}</textarea>
<p class="description">
此文字用于专题前述, 会在专题最前方显示.</p>
</li>
</ul>
<ul class="typecho-option">
<li>
<label class="typecho-label" for="blog-0-5">
<label class="typecho-label" for="blog-0-6">
归档前说</label>
<textarea id="blog-0-5" name="archivessay">{{.ArchivesSay}}</textarea>
<textarea id="blog-0-6" name="archivessay">{{.Blogger.ArchivesSay}}</textarea>
<p class="description">
此文字用于归档前述, 会在归档最前方显示.</p>
</li>
@@ -123,7 +149,7 @@
<section id="change-password">
<h3>密码修改</h3>
<form action="/admin/api/password" method="post" enctype="application/x-www-form-urlencoded">
<ul class="typecho-option" id="typecho-option-item-password-10">
<ul class="typecho-option" id="typecho-option-item-oldpwd-11">
<li>
<label class="typecho-label" for="password-0-11">
原始密码</label>
@@ -132,26 +158,26 @@
该账户旧密码.</p>
</li>
</ul>
<ul class="typecho-option" id="typecho-option-item-password-10">
<ul class="typecho-option" id="typecho-option-item-newpwd-12">
<li>
<label class="typecho-label" for="password-0-11">
<label class="typecho-label" for="password-0-12">
用户密码</label>
<input id="password-0-11" name="new" type="password" class="w-60" />
<input id="password-0-12" name="new" type="password" class="w-60" />
<p class="description">
为此用户分配一个密码.
<br />建议使用特殊字符与字母、数字的混编样式,以增加系统安全性.</p>
</li>
</ul>
<ul class="typecho-option" id="typecho-option-item-confirm-11">
<ul class="typecho-option" id="typecho-option-item-confirm-13">
<li>
<label class="typecho-label" for="confirm-0-12">
<label class="typecho-label" for="confirm-0-13">
用户密码确认</label>
<input id="confirm-0-12" name="confirm" type="password" class="w-60" />
<input id="confirm-0-13" name="confirm" type="password" class="w-60" />
<p class="description">
请确认你的密码, 与上面输入的密码保持一致.</p>
</li>
</ul>
<ul class="typecho-option typecho-option-submit" id="typecho-option-item-submit-13">
<ul class="typecho-option typecho-option-submit" id="typecho-option-item-submit-14">
<li>
<button type="submit" class="btn primary">
更新密码</button>

View File

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

View File

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

View File

@@ -51,8 +51,8 @@
<td>{{.Title}}</td>
<td>{{.Author}}</td>
<td>{{if gt .SerieID 0}}专题ID:{{.SerieID}}{{else}}--{{end}}</td>
<td>{{dateformat .CreateTime "2006/01/02 15:04"}}</td>
<td>{{dateformat .DeleteTime "2006/01/02 15:04"}}</td>
<td>{{dateformat .CreatedAt "2006/01/02 15:04"}}</td>
<td>{{dateformat .DeletedAt "2006/01/02 15:04"}}</td>
</tr>
{{end}}
</tbody>

View File

@@ -0,0 +1 @@
{{define "article"}}<div id=content class=inner>{{with .Article}}<article class="post post-{{.ID}}" itemscope itemtype="http://schema.org/Article"><div class=meta><div class=date><time itemprop=datePublished content="{{dateformat .CreatedAt "2006-01-02T15:04:05Z07:00"}}">{{dateformat .CreatedAt "Jan 02, 2006"}}</time></div><div class="date-modified"><time itemprop=dateModified content="{{dateformat .UpdatedAt "2006-01-02T15:04:05Z07:00"}}">{{dateformat .UpdatedAt "Jan 02, 2006"}}</time></div><div class=comment><a href="#comments">{{.Count}} Comments</a></div></div><h1 class=title itemprop=headline>{{.Title}}</h1><div class="entry-content" itemprop=articleBody>{{str2html .Header}}{{str2html .Content}}<p>本文链接:<a rel="bookmark" title="Permalink to {{.Title}}" href="//{{$.Domain}}/post/{{.Slug}}.html" itemprop="url">https://{{$.Domain}}/post/{{.Slug}}.html</a><a href="//{{$.Domain}}/post/{{.Slug}}.html#comments">参与评论 »</a></p><p>--<acronym title="End of File">EOF</acronym>--</p><p class="post-info">发表于 <span class="date">{{dateformat .CreatedAt "2006-01-02 15:04:05"}}</span>{{if gt (.Tags|len) 0}},并被添加「{{range $index, $elem := .Tags}}{{if gt $index 0}}、{{end}}<a href="/search.html?q=tag:{{$elem}}">{{$elem}}</a>{{end}}」标签{{end}}{{if .UpdatedAt|isnotzero}},最后修改于 <span class="date">{{dateformat .UpdatedAt "2006-01-02 15:04:05"}}</span>{{end}}。</p>{{with $.Copyright}}<p class="copyright-info">{{str2html $.Copyright}}<a href="//{{$.Domain}}/post/about.html#toc_1">更多说明 »</a></p>{{end}}{{if gt $.Days 100}}<p class="expire-tips">提醒:本文最后更新于 {{$.Days}} 天前,文中所描述的信息可能已发生改变,请谨慎使用。</p>{{end}}{{with $.Serie}}<div class="entry-series"><h3>专题「{{.Name}}」的其它文章 <a href="/series.html#toc-{{.ID}}" title="更多">»</a></h3><ul>{{range .Articles}}{{if ne .ID $.Article.ID}}<li><a href="/post/{{.Slug}}.html">{{.Title}}</a> <span class="date">({{dateformat .CreatedAt "Jan 02, 2006"}})</span></li>{{end}}{{end}}</ul></div>{{end}}</div></article><nav class="page-navi">{{with .Prev}}<a href="/post/{{.Slug}}.html" class=prev>« {{.Title}}</a>{{end}}{{with .Next}}<a href="/post/{{.Slug}}.html" class=next>{{.Title}} »</a>{{end}}</nav><section id=comments><h1 class=title>Comments</h1><div class=total_thread data-identifier="post-{{.Slug}}" data-url="https://{{$.Domain}}/post/{{.Slug}}.html"></div></section>{{end}}</div>{{end}}

View File

@@ -0,0 +1 @@
{{define "blogroll"}}<div id=content class=inner>{{with .Article}}<article class="post post-{{.ID}}" itemscope itemtype="http://schema.org/Article"><h1 class=title itemprop=headline>{{.Title}}</h1><div class="entry-content" itemprop=articleBody><style>.links.ssl li{position:relative;padding-left:22px}.links.ssl li::before{content:'';display:block;position:absolute;top:6px;left:0;width:16px;height:16px;background-size:16px 16px;background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAD/0lEQVR4Ab2XA5hjOxTHO9batrdce8yrtW2b9dgq1jae1vbueOpn2zbzcoqP06luJ993epXk/0vOOUnK8abkq6Ttb1r2UY8+PJzx4L1DZY8/PrzrxYqySYqsXeEcf5Z+I9r3WlI0MjfzVoKx4Gnaj4XP0pDd/sl/kvq1/HJsxXSxcHP7Hs1bsS4+fSt3RXkt9ZVaz6DyehqpDTTSWI3503bF7+sopDEyqOBxqnks02Mia+IzxTyp1jwZlVSRVlHpS3F3qfWDV49I7TZmFNGdP4rqHj1LIhDn3Es2qfU0Kq0hAfLncVN7pvksTq0fNH+fxSZeVkN+kbSk/0xnddt1jYyanyGSaoz031BfpaO+5U7qyPVavAe3VdeSavIzq3gt+RUe0Qg3Z2wFnol/wSXSl+NuRzQPCfIKYGnOCDn4FHf2H71+8CJP2q7fN3Y/xIrWxKCYWX0SPBZv2SkyPOd+qlFVTyH5lfjaiGYhwZ607y1o07e0mvxBraPRhiMTD3qecsPbDsDB9CtE93ylUMLxouy+EHNLjdsrriXoIluGBrrdMDAoMHjslB7RkGoQ1ROm95wRGBQQFBwSGOqu4fqBC7OGlWuNDKwTH3Qf3KpLUHBgSKPCe8/u5G0+Lri343S0Hk/7e0UVxH9gslfi3ttxepJu59loo7sG9TNvJX5RXEn8hwH+FF+KfQ2/N6zbxz11+9Gl1g0C3Hm/5GB+VRyOeArSDvIZ7mEWIJg8NlU9beurGveFDe7zq+LRa9/cndsgwPU3C09kPYq2Lq+Y/C/xC7FPdpyZdAuT3/HW8EzcVF5NMJRUEdZ+oX/TF7eXOgM4BhWAfFX5aA2HpdK2a1SE4mpCJZ4JB8DiRgEg8meJBWs5LBY8E+fBBW4DzJEJN3s12u5RUQ29x+645HeAntzW3fIfp9bMkQv2NDlAL17rbtn3kusdy+88pXB3UwGAeA8Qh82n8Gka2mue3HQANvEkPYhDmmnNDMK74Va/uKBF27DQlWWjFH1Fbfs4F+dv9UsQtu0a2XrrsQmnD745FSmvJ1RNmNFrEr7Wldc7F2cVIGFxP/rw29MQPoxal2ks/BdcnYuzDBASFhQ0cw9/J5wNi+ynYbjC8ywxf4sTXfaDEIvZIJ4TIA4j3wTvmwrAAbH70NvTfnFn5H5Lw1FEt+Fw9RvAbIlgA6ub0ZnoC24DwL8ffJwSswmAT0Q3SmtcAxyHCvgYhnIfpnyGITJnywQ78Oay21ubLRVsx2cLbXEF8Xvh87TGAW6+U3gq8+FElP80GRU+T0HlOhKp9L5bWT2B148Ua79Zjyciy9dOTkTnKwqogidp76mqZnyjqpzxNdumrp75bfa9lNqK1272d2j+D+LY/CDPYPo9AAAAAElFTkSuQmCC)}.links li .more{color:#999;font-size:14px}</style>{{str2html .Content}}<p>注:为了节约本站用户的宝贵时间,长期无法访问的链接会被移除!另本站只有友情链接,不接受链接交换。</p></div></article>{{end}}</div>{{end}}

View File

@@ -0,0 +1 @@
<!Doctype html><html lang="zh-CN"><head><meta charset="utf-8"><meta content="width=device-width,minimum-scale=1.0" name=viewport><meta name="format-detection" content="telephone=no"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name=referrer content=always><meta name=robots content="noindex, nofollow, noarchive"><title>{{.Title}}</title><style type="text/css">*{margin:0;padding:0}html,body{height:100%}body{background:#fff;color:#2a2e2e;font-size:15px;font-family:"Helvetica Neue",arial,sans-serif;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}::selection,::-moz-selection,::-webkit-selection{background-color:#2479CC;color:#eee}h3{font-size:1.3em;line-height:1.5;margin:15px 30px;text-align:center}a{color:#2479CC;text-decoration:none}.card{margin:15px 25px;text-align:left}.submit input,.submit textarea{-webkit-appearance:none;border:1px solid #bbb;border-radius:1px;font-size:15px;height:20px;line-height:20px;margin-left:10px;padding:6px 8px}.submit span{position:relative;top:8px}.submit li{display:-webkit-box;display:-ms-flexbox;display:flex;margin:15px 0}.submit textarea{height:130px}.submit .line{-webkit-box-flex:1;display:block;-ms-flex:1;flex:1}.submit .btn-submit{-webkit-appearance:none;background:#12b0e6;border:none;border-radius:0;box-shadow:inset 0 -5px 20px rgba(0,0,0,.1);color:#fff;cursor:pointer;display:block;font-size:14px;line-height:1;padding:0.625em .5em;width:100%}.submit li.tips{color:#999;font-size:14px}</style></head><body><header><h3>对「{{.ATitle}}」发表评论</h3></header><div class=bd><div class="card submit"><form onsubmit="return false" id="create-post"><ul><li><span>昵称:</span><input class=line name=author_name required placeholder="昵称会被公开显示"><li><span>邮箱:</span><input class=line name=author_email type=email required placeholder="邮箱不会公开显示"><li><span>内容:</span><textarea class="line" name="message" required placeholder="请不要发表无意义的评论内容"></textarea><li><input type=hidden name=thread value="{{.Thread}}"><input type=hidden name=parent value=""><input type=hidden name=identifier value="post-{{.Slug}}"><input type=hidden name=next value=""><button class="btn-submit" type=submit>立即发表</button><li class=tips>注:通过本表单提交的数据,会原样转发给 Disqus本站不做任何存储和记录。<li><a href="#close" onclick="window.close();void(0)">放弃评论</a></ul></form></div></div><footer></footer><script src="https://cdn.bootcss.com/jquery/3.1.1/jquery.min.js" ></script><script>!function(a){function e(){try{localStorage.author_name=$('[name="author_name"]').val(),localStorage.author_email=$('[name="author_email"]').val()}catch(a){}}function t(){$('[name="author_name"]').val(localStorage.author_name),$('[name="author_email"]').val(localStorage.author_email),["author_name","author_email","message"].some(function(a){var e=$('[name="'+a+'"]');return e.val()?void 0:e.focus()})}var o=!1;$("#create-post").on("submit",function(e){if(e.preventDefault(),!o){o=!0;var t=$(".tips");t.html("提交中..."),$.post("/disqus/create",$("#create-post").serialize()).then(function(e){o=!1,e.errno?t.html("提交失败:"+e.errmsg):($(".btn-submit").prop("disabled",!0),t.html("提交成功!本窗口即将关闭。"),setTimeout(function(){try{a.opener.location.hash="comment-"+e.data.id,a.opener.TotalThread.currentServer.insertItem(e.data),a.close()}catch(t){a.close()}},1e3))})}}),t(),$('[name="author_name"], [name="author_email"]').on("change",e)}(this)</script></body></html>

View File

@@ -0,0 +1 @@
{{define "home"}}<div id="content" class="inner">{{range .List}}<article class="post post-list"><div class="meta"><div class="date"><time>{{dateformat .CreatedAt "Jan 02, 2006"}}</time></div><div class="comment"><a href="/post/{{.Slug}}.html#comments">{{.Count}} Comments</a></div></div><h1 class="title"><a href="/post/{{.Slug}}.html">{{.Title}}</a></h1><div class="entry-content"><p>{{str2html .Excerpt}}[...]</p><p><a href="/post/{{.Slug}}.html" class="more-link">继续阅读 »</a></p></div></article>{{end}}<nav class="page-navi">{{if gt .Prev 0}}<a href="?pn={{.Prev}}" class="prev">« 上一页</a>{{end}}{{if gt .Next 0}}<a href="?pn={{.Next}}" class="next">下一页 »</a>{{end}}<div class="center"><a href="/archives.html">博客归档</a></div></nav></div>{{end}}

View File

@@ -0,0 +1 @@
<!DOCTYPE html><html lang="zh-cn"><head><meta charset="utf-8"><meta content="width=device-width,minimum-scale=1.0" name="viewport"><meta name="format-detection" content="telephone=no"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="referrer" content="always"><title>{{.Title}}</title>{{.AdSense}}<script>!function(n,t){function e(){o("nls",1)}function c(){t.documentElement.style.display="none",u(),location.reload()}function r(n){var t="";try{t=f[n]||"",t.length<99&&c()}catch(e){u()}return t}function i(n,t){try{f[n]=t,t!==f[n]&&u()}catch(e){u()}}function o(n,e){var c=999;e||(c=-1),c=new Date(+new Date+864e5*c).toGMTString();var r=n+"="+e+";path=/;secure;expires="+c;t.cookie=r}function a(n){var e=t.getElementById(n).innerHTML;i(n,e)}function l(e,c){var i=r(e),o=t.createElement(c?"script":"style");return n.execScript&&c?n.execScript(i):(o.innerHTML=i,void t.head.appendChild(o))}function u(){o("v",0)}var f,h=function(){},d=n.L={h:h,l:h,c:h};try{f=localStorage,d.h=a,d.l=l,d.c=o}catch(p){e()}}(this,document);</script>{{if .Version}}<script>L.c('v', {{.Version}})</script>{{end}}<link rel="apple-touch-icon" href="//{{.Qiniu.Domain}}/static/img/favicon.ico"><link rel="search" type="application/opensearchdescription+xml" href="//{{.Domain}}/opensearch.xml" title="{{.BTitle}}">{{if .Version}}<style id="blog_css">{{template "blog_css" .}}</style><script>L.h('blog_css')</script>{{else}}<script>L.l("blog_css")</script>{{end}}<link rel="alternate" type="application/rss+xml" title="RSS 2.0" href="//{{.Domain}}/rss.html"><meta name="description" content="{{.Description}}"><meta name="twitter:card" content={{.Twitter.Card}}><meta name="twitter:site" content="@{{.Twitter.Site}}"><meta name="twitter:title" content="{{.Title}}"><meta name="twitter:description" content="{{.Description}}"><meta name="twitter:image" content="https://{{.Twitter.Image}}"></head><body><div class="container"><div class="left-col"><div class="intrude-less"><header id="header" class="inner"><div class="profilepic"><a href="/"></a></div><h1><a href="/">{{.BlogName}}</a></h1><p class="subtitle">{{.SubTitle}}</p><nav id="main-nav"><ul><li {{if eq .Path "/"}}class="on" {{end}}><a href="/"><span>首页</span></a></li><li {{if eq .Path "/series.html"}}class="on" {{end}}><a href="/series.html"><span>专题</span></a></li><li {{if eq .Path "/archives.html"}}class="on" {{end}}><a href="/archives.html"><span>归档</span></a></li><li {{if eq .Path "/post/blogroll.html"}}class="on" {{end}}><a href="/post/blogroll.html"><span>友链</span></a></li><li {{if eq .Path "/post/about.html"}}class="on" {{end}}><a href="/post/about.html"><span>关于</span></a></li>{{range $index, $element := .Pages}}{{if .ShowInNav}}<li {{if eq $.Path .Path}}class="on" {{end}}><a href="{{.Path}}" {{if not .IsEmbed}}target="_blank"{{end}}><span>{{.Name}}</span></a></li>{{end}}{{end}}</ul></nav><nav id="sub-nav"><div class="social"><a target="_blank" class="twitter external" rel="nofollow" href="//{{.Twitter.Address}}" title="Twitter" aria-label="Twitter">Twitter</a><a target="_blank" class="rss" href="//{{.Domain}}/rss.html" title="RSS 订阅" aria-label="RSS 订阅">RSS</a><a class="search" href="/search.html" title="站内搜索" aria-label="站内搜索">Search</a></div></nav></header></div></div><div class="mid-col"><div class="mid-col-container">{{.LayoutContent}}</div><footer id=footer class=inner>© {{.CopyYear}} - {{.BTitle}}{{if .BeiAn}} - <a target="_blank" rel="nofollow designer" class="external beian" href="https://beian.miit.gov.cn">{{.BeiAn}}</a>{{end}}<br>Powered by <a target=_blank href="//github.com/eiblog/eiblog">Eiblog</a> & <a target=_blank rel="nofollow designer" class=external href="//github.com/deepzz0">Deepzz</a></footer></div></div><input type=hidden id=CURRENT_PAGE value="{{.CurrentPage}}"><input type=hidden id=CDN_DOMAIN value="//{{.Qiniu.Domain}}">{{if .Version}}<script id="ana_js">{{template "ana_js"}}</script><script>L.h('ana_js')</script>{{else}}<script>L.l('ana_js', 1)</script>{{end}}{{if .Version}}<script id="jq_js">{{template "jq_js"}}</script><script>L.h('jq_js')</script>{{else}}<script>L.l('jq_js', 1)</script>{{end}}{{if .Version}}<script id="hl_js">{{template "hl_js"}}</script><script>L.h('hl_js')</script>{{else}}<script>L.l('hl_js', 1)</script>{{end}}{{if .Version}}<script id="blog_js">{{template "blog_js" .}}</script><script>L.h('blog_js')</script>{{else}}<script>L.l('blog_js', 1)</script>{{end}}</body></html>

View File

@@ -0,0 +1 @@
{{define "search"}}<div id="content" class="inner"><article class="post post-search"><h1 class="title">站内搜索</h1><div class="entry-content"><div id="search"><form action="/search.html"><div class="wrapper"><input maxlength="80" placeholder="请输入关键字..." id="keyword" name="q" {{if .Word}}value="{{.Word}}"{{end}} type="search" required></div><input class="submit" type="submit" value="搜索"></form></div><div id="searchResult">{{if .Word}}{{with .SearchResult}}{{if gt (.Hits.Hits|len) 0}}<div class="info">本次搜索共找到结果 {{.Hits.Total}} 条 (用时 {{.Took}} 秒)</div>{{range .Hits.Hits}}<div class="item"><div class="title"><a href="/post/{{.Source.Slug}}.html">{{if .Highlight.Title}}{{str2html (join .Highlight.Title "")}}{{else}}{{.Source.Title}}{{end}}</a></div><div class="desc">{{if .Source.Img}}<div class="img"><img data-src="{{.Source.Img}}?imageView2/1/w/216/h/162"></div>{{end}}<div class="summary"><span class="date">{{dateformat .Source.Date "2006-01-02"}}</span> ... {{str2html (join .Highlight.Content "...")}} ...</div></div></div>{{end}}{{else}}<div class="no-result">没有找到任何结果,请更换查询词试试~</div><div class="item"><div class="title">或者试试 Google 站内搜索:<a target="_blank" href="//www.google.com/#q=site:{{$.Domain}} {{$.Word}}">site:{{$.Domain}} {{$.Word}}</a></div></div>{{end}}{{end}}{{else}}<div class="hot-words">热搜词:{{range .HotWords}}<a href="?q={{.}}">{{.}}</a>{{end}}</div><div class="intro"><p>支持的搜索格式:</p><ol><li>输入关键词全文搜索:<a href="?q=Let's Encrypt">Let's Encrypt</a></li><li>指定时间段搜索:<a href="?q=date:2016">date:2016</a><a href="?q=date:2016-10">date:2016-10</a></li><li>指定标签搜索:<a href="?q=tag:github">tag:github</a><a href="?q=tag:HTTPS">tag:HTTPS</a></li><li>组合搜索:<a href="?q=date:2016 tag:docker">date:2016 tag:docker</a></li></ol></div>{{end}}</div></div></article>{{if or .Prev .Next}}<nav class="page-navi">{{with .Prev}}<a href="?{{html .}}" class="prev">« 上一页</a>{{end}}{{with .Next}}<a href="?{{html .}}" class="next">下一页 »</a>{{end}}</nav>{{end}}</div>{{end}}

View File

@@ -0,0 +1,3 @@
{{define "ana_js"}}
!function(e,n,o){var t=e.screen,a=encodeURIComponent,r=["dt="+a(n.title),"dr="+a(n.referrer),"ul="+(o.language||o.browserLanguage).toLowerCase(),"sd="+t.colorDepth+"-bit","sr="+t.width+"x"+t.height,"_="+ +new Date],i="?"+r.join("&");e.__beacon_img=new Image,e.__beacon_img.src="/beacon.html"+i}(window,document,navigator,location);
{{end}}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,4 @@
<?xml version="1.0"?>
<cross-domain-policy>
<allow-access-from domain="*.{{.Host}}" />
</cross-domain-policy>

View File

@@ -0,0 +1,24 @@
<?xml version="1.0" encoding="utf-8" ?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
<channel>
<title>{{.Title}}</title>
<link>https://{{.Host}}</link>
<description>{{.SubTitle}}</description>
<atom:link href="https://{{.Host}}/rss.html" rel="self" />
<atom:link href="{{.FeedrURL}}" rel="hub" />
<language>zh-CN</language>
<lastBuildDate>{{.BuildDate}}</lastBuildDate>
{{range .Articles}}
<item>
<title>{{.Title}}</title>
<link>https://{{$.Host}}/post/{{.Slug}}.html</link>
<comments>https://{{$.Host}}/post/{{.Slug}}.html#comments</comments>
<guid>https://{{$.Host}}/post/{{.Slug}}.html</guid>
<description>
<![CDATA[{{imgtonormal .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>
<pubDate>{{dateformat .CreatedAt "Mon, 02 Jan 2006 15:04:05 -0700"}}</pubDate>
</item>
{{end}}
</channel>
</rss>

View File

@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8" ?>
<OpenSearchDescription xmlns="http://a9.com/-/spec/opensearch/1.1/"
xmlns:moz="http://www.mozilla.org/2006/browser/search/">
<ShortName>{{.BTitle}}</ShortName>
<Description>{{.SubTitle}}</Description>
<InputEncoding>UTF-8</InputEncoding>
<Url type="text/html" method="get" template="https://{{.Host}}/search.html?q={searchTerms}" />
<moz:SearchForm>https://{{.Host}}/search.html</moz:SearchForm>
</OpenSearchDescription>

View File

@@ -0,0 +1,3 @@
User-agent: *
Allow: /
Sitemap: https://{{.Host}}/sitemap.xml

Some files were not shown because too many files have changed in this diff Show More