自动部署传统的hexo-admin使用deploy是调用以下语句,在本地生成并部署到服务器
1 2 3 hexo clean hexo g hexo d
但是我的博客使用双仓库模式,即建立blog
和blog-deploy
两个仓库。
blog
仓库用于储存文章、图片等源码信息。blog-deploy
用于发布博客。即hexo d的目标仓库后来发现我现在创建的blog-deploy
是多余的,因为在服务器自动部署后,直接移动/复制到blog-deploy
目录即可,不用产生额外的提交。
只需要直接提交代码到服务器,然后由服务器执行部署代码即可。创建push.bat
,代码如下
1 2 3 git add . git commit -m %1 git push
对于部分linux和osx等系统,可能不兼容bat
文件,可能需要修改为*.sh
hexo-admin-deploy在hexo-admin点击Deploy
就会自动执部署脚本。
编辑_config.yml
增加以下内容。
1 2 3 admin : deployCommand: 'push.bat'
服务端拉取更新关键技术是使用githook
。 服务器监听一个端口,git更新代码后会发送一个http请求到该地址,服务器识别并执行预设代码。 下面是部署的关键代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 <?php $blog = [ "pull" => true , "script" => [ "echo now Ready hexo clean" , "hexo clean" , "echo now Ready hexo generate" , "hexo g" , "echo \\rm -rf ..../blog-deploy/*" , "\\rm -rf ../blog-deploy/*" , "echo markBuildTime" , "date +'%Y/%m/%d %H:%M:%S'>../blog-deploy/build.txt" , "echo now Ready Copy to deploy" , "\\cp -rf public/* ../blog-deploy/" ] ]; $commandLine = implode(" && " , $blog["script" ]); exec( $commandLine, $execResult ); print_r( $execResult );
由于存在其他系统变量,上述代码可能无法执行,仅供参考。
在页面中显示部署时间前面已经在服务器拉去时使用"date +'%Y/%m/%d %H:%M:%S'>../blog-deploy/build.txt",
生成构建时间,现在想把该时间显到页面中 打开/source/about/index.md
文件,在文件最后面增加
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 <script type ="text/javascript" > function addLoadEvent (func) { var oldonload = window .onload; if (typeof window .onload != 'function' ) { window .onload = func; } else { window .onload = function ( ) { oldonload(); func(); } } } addLoadEvent( () => {getBuildTime()} ) function getBuildTime ( path="../build.txt" ) { $.get ( path, (e)=>{ console .log("获得生成时间成功" ) let build = document .createElement("div" ); let hr = document .createElement("hr" ); let buildTime = document .createElement("span" ); let TextNode = document .createTextNode("This Website Was Generated On " ); buildTime.innerText = e; build.append( TextNode, buildTime ); $(".article-body" ).append( hr, build ) }).error(e => { console .log("获得生成时间失败" ) let hr = document .createElement("hr" ); let build = document .createElement("div" ); let TextNode = document .createTextNode("Failed To Get Build Time." ); build.append( TextNode ); $(".article-body" ).append( hr, build ) }); }</script >
由于显示文章内容时,全局函数还没加载,因此新增addLoadEvent执行队列。
hexo-admin这里使用的是hexo-admin@2.3.0
cdn优化hexo-admin
在引入jquery
时默认使用的是//ajax.aspnetcdn.com
的CDN。 通过nslookup 查询到该域名指向117.18.232.200
通过淘宝IP数据库 查询到该IP地址指向美国密苏里
,在国内环境可能不稳定。
所以需要把./node_modules/hexo-admin/www/index.html
第10行的ajax.aspnetcdn.com
更改为国内的CDN。我这里是直接把该行注释,并增加百度智能云
提供的CDN。
1 2 <script src ="//apps.bdimg.com/libs/jquery/1.9.0/jquery.min.js" > </script >
windows下插入图片问题在windows的环境使用hexo-admin,可能在插入图片时会产生错误。如直接粘贴图片时,会在编辑区域生成以下内容,右侧的区域也无法正常查看。
1 
解决方案: 修改./node_modules/hexo-admin/api.js
的第388
和400
行。
1 2 3 filename = imagePath + "/" + filename
修改以后,图片可以正常插入,格式为
1 
如果想修改默认目录和前缀,可以到Hexo-admin
的Setting
中修改:
现在插入图片有可能由于写入等原因,图片可能没有马上展示。继续编辑文章或者保存刷新即可正常显示图片,不影响写作。
图片优化使用tinify
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 import tinifyimport os; tinify.key = "xxx" absFile = os.path.abspath(__file__); absPath = os.path.dirname(absFile); relPath = "../../source/images-origin/" ; currentDir = (os.path.abspath(os.path.join( absPath,relPath ))); supportImgType = ['.jpg' ,'.png' ];for item in os.listdir(currentDir): fulldirct = os.path.join(currentDir,item) if os.path.isfile(fulldirct): print('doing:' +fulldirct) if os.path.splitext(item)[1 ] in supportImgType: source = tinify.from_file(fulldirct) source.to_file(fulldirct) print('done:' +fulldirct)
增加预览支持默认情况下hexo-admin的posts编辑实时预览与pure的主题不完全一致,需要对hexo-admin的主题进行修改。
对/node_modules/hexo-admin/www/index.html
进行修改,在任意位置增加以下CSS。具体CSS根据文章页面的加载内容进行hack。
1 2 3 4 <link rel ="stylesheet" href ="/css/style.css" > <link rel ="stylesheet" href ="/css/theme/common.css" > <link rel ="stylesheet" href ="/css/theme/monokai-hook.css" > <link rel ="stylesheet" href ="/css/theme/monokai.css" >
目前存在bug: 修改文章,右侧预览页面代码高亮失效。需要刷新页面重新生效。
pure主题本章节是对cofess/hexo-theme-pure
进行优化和修改的记录,并没有提交到仓库。
代码高亮在cofess/hexo-theme-pure
主题中,使用的是默认主题。由于博客背景色是#ffffff
,代码块的背景色默认为#fafafa
。造成代码块与博客普通文本区分度不高。 这次使用的是monokai.css
主题,但是直接引入这个主题与博客代码并不兼容,所以需要卸载默认的markdown引擎和安装CHENXCHEN/hexo-renderer-markdown-it-plus 。通过配置文件,为代码块的类名增加前置名称hljs
。 卸载与安装命令如下:
1 2 npm un hexo-renderer-marked --save npm i hexo-renderer-markdown-it-plus --save
在hexo根目录配置_config.yml
,增加/修改内容:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 markdown_it_plus: highlight: true html: true xhtmlOut: true breaks: true langPrefix: linkify: true typographer: quotes: “”‘’ pre_class: hljs plugins: - plugin: name: markdown-it-katex enable: true
通过Chrome DevTools
查看到,页面code标签以及子类已经成功添加hljs
,当页面由于还没有引入主题文件。
下载monokai.css 主题文件(其他主题可以到 选择),将该文件令存在保存到/themes/pure/source/css
在/themes/pure/source/css
目录新增monokai-hook.css
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 @import url(monokai.css);.highlight , pre { overflow-y : hidden; background-color : #272822 ; line-height : 24px ; }.highlight :before { background-color : #272822 ; }.highlight table >tbody >tr :nth-of-type(odd) { background-color : #272822 ; }
编辑/themes/pure/layout/_common/head.ejs
,在53行
附近的<%- css('css/style') %>
下面新增引入monokai-hook文件的css。
1 2 <%- css ('css /theme /monokai-hook ') %>
修改完成后的效果
代码高亮的滚动条1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 .highlight ::-webkit-scrollbar { height : 8px ; }.highlight ::-webkit-scrollbar-thumb { border-radius : 45px ; background-color : #6f6969 ; background-image : -webkit-linear-gradient (45deg , rgba(255 , 255 , 255 , 0.2 ) 12.5% , transparent 12.5% , transparent 25% , rgba (255 , 255 , 255 , 0.2 ) 25% , rgba (255 , 255 , 255 , 0.2 ) 37.5% , transparent 37.5% , transparent 50% , rgba (255 , 255 , 255 , 0.2 ) 50% , rgba (255 , 255 , 255 , 0.2 ) 62.5% , transparent 62.5% , transparent 75% , rgba (255 , 255 , 255 , 0.2 ) 75% , rgba (255 , 255 , 255 , 0.2 ) 87.5% , transparent 87.5% ); }.highlight ::-webkit-scrollbar-track { -webkit-box-shadow : inset 0 0 6px rgba (0 , 0 , 0 , 0.3 ); border-radius : 10px ; background-color : #F5F5F5 ; }
修改代码块字体cofess/hexo-theme-pure
代码块的默认字体是monospace,monospace
,如果想修改为其他字体,需要对相关代码进行重写。 编辑/themes/pure/source/css/style.css
,在最后增加以下代码即可。
cofess/hexo-theme-pure
1 2 3 4 5 6 7 8 9 10 11 .highlight .gutter pre { font-family : Inconsolata; }.highlight .code code { font-family : Inconsolata; }
修改##
的样式cofess/hexo-theme-pure
对h2
的样式进行了修改,由于导致##
和###
区别不大,通过编辑/themes/pure/source/css/style.css
,实现对##
的样式的修改。
1 2 3 .marked-body h2 { border-bottom : 2px solid #d6d5d5 ; }
增加首页备案号cofess/hexo-theme-pure
默认是没有对备案号进行处理的,所以需要在/themes/pure/layout/_partial/archive-list.ejs
追加备案号。
1 2 3 4 5 <% if (theme.beian ) { %> <div class ="footer_copyright" > <span > <a href ='https://beian.miit.gov.cn' target ='_blank' > <%- theme.beian %> </a > </span > </div > <% } %>
然后编辑主题的css文件style.css
1 2 3 4 5 6 7 8 .footer_copyright { position : fixed; text-align : center; bottom : 10px ; width : 100% ; z-index : 999 ; left : 0 ; }
为代码块增加复制按钮参考文章:next主题添加代码块一键复制功能
1、增加全局函数addLoadEvent 在/themes/pure/source/js
目录下打开application.js
,在文件最后追加
1 2 3 4 5 6 7 8 9 10 11 function addLoadEvent (func ) { var oldonload = window .onload; if (typeof window .onload != 'function' ) { window .onload = func; } else { window .onload = function ( ) { oldonload(); func(); } } }
2、新增按钮: pure默认情况下是没有代码复制功能的,此时需要对hexo增加复制代码块功能。 首先在/themes/pure/layout/_partial
目录下新增article-copy-code.ejs
,增加以下内容:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 <% if (theme.codeblock.copy_button.enable ){ %> <style > .copy-btn { display: inline-block; padding: 6px 12px; font-size: 13px; font-weight: 700; line-height: 20px; color : #333 ; white-space: nowrap; vertical-align: middle; cursor: pointer; background-color : #eee ; background-image : linear-gradient (#fcfcfc , #eee ); border : 1px solid #d5d5d5 ; border-radius: 3px; user-select: none; outline: 0; } .highlight-wrap .copy-btn { transition : opacity .3s ease-in-out ; opacity: 0; padding: 2px 6px; position: absolute; right: 4px; top: 8px; z-index: 2; } .highlight-wrap :hover .copy-btn , .highlight-wrap .copy-btn :focus { opacity: 1 } .highlight-wrap { position: relative; } </style > <script > addLoadEvent(() => { $('.highlight' ).each(function (i, e ) { var $wrap = $('<div>' ).addClass('highlight-wrap' ) $(e).after($wrap) $wrap.append($('<button>' ).addClass('copy-btn' ).append('<%= __("codeblock.copy_button") %>' ).on('click' , function (e ) { var code = $(this ).parent().find(".code" )[0 ].innerText <% if (theme.codeblock.copyright.enable ){ %> code += "<%= theme.codeblock.copyright.content %>" <% } %> var ta = document .createElement('textarea' ) document .body.appendChild(ta) ta.style.position = 'absolute' ta.style.top = '0px' ta.style.left = '0px' ta.value = code ta.select() ta.focus() var result = document .execCommand('copy' ) document .body.removeChild(ta) <% if (theme.codeblock.copy_button.result ){ %> if (result)$(this ).text('<%= __("codeblock.copy_success") %>' ) else $(this ).text('<%= __("codeblock.copy_failure") %>' ) <% } %> $(this ).blur() })).on('mouseleave' , function (e) { var $b = $(this ).find('.copy-btn' ) setTimeout(function () { $b.text('<%= __("codeblock.copy_button") %>' ) }, 300) }).append(e) }) }) </script > <% } %>
3、插入到页面: 编辑/themes/pure/layout/layout.ejs
,在</body>
前面一行增加<%- partial('_partial/article-copy-code')%>
1 2 3 4 5 6 <%- body %> <%- partial('_common/footer', null, {cache: !config.relative_link}) %> <%- partial('_common/script', {post: page}) %> <%- partial('_partial/article-copy-code') %> </body> </html>
坑:理论上可以在其他地方添加,但是必须保证代码在jq以及页面dom加载后运行。
4、增加语言文件: 在/themes/pure/languages
目录下选择对应的语言文件,在文件后面增加:
1 2 3 4 codeblock: copy_button: 复制 copy_success: 复制成功 copy_failure: 复制失败
理论上只需添加当前语言文件即可,但是为了以后方便切换语音,建议所有语音文件都加上相应的配置。
5、增加主题配置文件 打开themes/pure/_config.yml
,在文件末尾添加
1 2 3 4 5 6 7 codeblock: copy_button: enable: true result: true copyright: enable: true content: \n/**\n* 感谢您复制代码,使用代码请注明引用出处\n* kajweb @ https://blog.iwwee.com\n*/
增加访问IP记录及页面跟踪2020-10-20
功能:
1、leancloud
创建Class 导入备份文件 并命名为Log
2、创建云引擎 云函数代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 var AV = require ('leanengine' ); AV.Cloud.define('recorder' , async function (request ) { let params = { ip: request.meta.remoteAddress, title: request.params.title, url: request.params.url, host: request.params.host, referrer: request.params.referrer, track: request.params.blogTrack, trackId: request.params.blogTrackId };return AV.Cloud.run('getCityByIp' , { ip : params.ip }).then(ipInfo => { return AV.Cloud.run( 'recorderSaveToDb' , Object .assign( params, { ipInfo } ) ); }).then(e => { return { id : e.getObjectId() }; }).catch(e => { console .log( e ) return Promise .reject("track服务异常" ); }) }); AV.Cloud.define('getCityByIp' , async function (request ) { let ip = request.params.ip || request.meta.remoteAddress;return AV.Cloud.run('getCityByIpFromTaobaoIp' , { ip }) .then(e => e.data); }); AV.Cloud.define('getCityByIpFromTaobaoIp' , async function (request ) { let ip = request.params.ip || request.meta.remoteAddress;let accessKey = request.params.accessKey || "alibaba-inc" ;let baseUrl = "http://ip.taobao.com/service/getIpInfo.php" ;let url = `${baseUrl} ?ip=${ip} &accessKey=${accessKey} ` ;var request = require ('superagent' )return request.get( url ).then(res => { return res.body; }).catch(e => { console .log("【淘宝】获得IP信息异常" ); return Promise .reject("获得IP信息异常" ); }); }); AV.Cloud.define('recorderSaveToDb' , async function (request ) { let ipInfo = request.params.ipInfo;delete request.params.ipInfo;var Loger = AV.Object.extend("Log" );var ipLoger = new Loger(); ipLoger.set(request.params); ipLoger.set(ipInfo);return ipLoger.save() }); AV.Cloud.define('trackUnload' , async function (request ) { let id = request.params.id;var query = new AV.Query("Log" );return query.get(id).then(e => { let createdAt = e.getCreatedAt(); let updatedAt = e.getUpdatedAt(); let timer = e.get("timer" ); if (timer) { let err = `该页面在${updatedAt} 已经被注销【${timer} 】` console .log(err); return Promise .reject(err); } let calcTimer = (new Date () - new Date (createdAt)) / 1000 ; e.set("timer" , calcTimer); return e.save(); }).then(e => { return { timer : e.get("timer" )}; }).catch(e => { console .log(e) return Promise .reject("处理失败" ); }) });
3、修改主题配置文件 进入/themes/pure
目录,编辑_config.yml
文件。在任意地方增加
1 2 3 4 5 loger: leancloud: enable: true app_id: xxxxxxxxxxxxxxxxxxxxxxxxx app_key: xxxxxxxxxxxxxxxxxxxxxxxx
4、创建loger.ejs
进入/themes/pure/layout/_script
目录,新建loger.ejs
文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 <% if (theme.loger.leancloud.enable ){ %> <script src="https://cdn1.lncld.net/static/js/av-min-1.2.1.js" ></script> <script defer> if( !AV.applicationId || AV.applicationKey ){ AV.init({ appId: '<%= theme.pv.leancloud.app_id %>', appKey: '<%= theme.pv.leancloud.app_key %>', }); } function randomString(len) { len = len || 32; / ****默认去掉了容易混淆的字符oOLl,9 gq,Vv,Uu,I1****/ var $chars = 'ABCDEFGHJKMNPQRSTWXYZabcdefhijkmnprstwxyz2345678'; var maxPos = $chars.length; var pwd = ''; for (i = 0; i < len; i++) { pwd += $chars.charAt(Math.floor(Math.random() * maxPos)); } return pwd; } function trackUnload( tracker ){ return ()=>{ AV.Cloud.run( 'trackUnload', tracker ).then( tracked=>{ console.log( `[track] ${tracker.id} 结束访问 ${tracked.timer}s` ) }).catch(()=>{ console.log( "退出服务异常" ) }) } } $(function() { let blogTrack = sessionStorage.getItem( 'blogTrack' ); let blogTrackId = sessionStorage.getItem( 'blogTrackId' ); if( !blogTrack ){ blogTrack = 1; blogTrackId = randomString(); sessionStorage.setItem( 'blogTrackId', blogTrackId ); } else { blogTrack = parseInt(blogTrack) + 1; } sessionStorage.setItem( 'blogTrack', blogTrack ); let params = { host: window.location.host, url: window.location.pathname, title: document.title, referrer: document.referrer, blogTrack, blogTrackId } AV.Cloud.run( 'recorder', params ).then( tracker=>{ console.log( `[track] ${tracker.id} 开始访问` ) window.onbeforeunload = trackUnload(tracker); }).catch(()=>{ console.log( "track服务异常" ) }) }); </ script> <% } %>
5、插入到全站代码
进入/themes/pure/layout/_common
目录,编辑script.ejs
文件,在后面增加
1 <%- partial ('_script /loger ') %>
增加移到滚动到顶端按钮1、在/themes/pure/layout/_common/script.ejs
增加
1 <%- partial ('_script /go-top ') %>
2、在/themes/pure/layout/_script
目录新增go-top.ejs
。在文件里面新增以下内容:
1 <div id ="go-top" > </div > <style type ="text/css" > #go-top {width :40px ;height :36px ;background-color :plum;position :relative;border-radius :2px ;position :fixed;right :10px ;bottom :60px ;cursor :pointer;display :none} #go-top :after {content :" " ;position :absolute;left :14px ;top :14px ;border-top :2px solid #fff ;border-right :2px solid #fff ;width :12px ;height :12px ;transform :rotate (-45deg )}#go-top :hover {background-color :#8a2be2 }</style > <script > $(function ( ) { var o = $("#go-top" );$(window ).scroll(function ( ) { 300 < $(window ).scrollTop() ? o.show(300 ) : o.hide(200 ), $("#go-top" ).click(function ( ) { return $("body,html" ).animate({ scrollTop : 0 }) }) }) }) </script >