使用Dropbox同步hexo文章

继之前那次失败的尝试之后(只在当时写的时候实验过几次,每次都以服务器卡死结束),后来在又多了几篇日志之后连generate也不能愉快的完成了。索性就在本地生成然后git push到服务器。

现在想更激进一些,git只管理日志以外的东西,比如hexo的升级,或模板的调整和日志源文件。而生成的静态文件直接通过Dropbox客户端同步到服务器。

话不多说。

以下为前提:

本地已安装hexo,和Dropbox客户端,并且客户端的同步目录已经选择到hexo的目录。
服务器已安装dropbox服务,及相应的用户。

Dropbox的同步目录选hexo根目录或public都行,只是在服务器的处理脚本那同步修改下就行了。

以下内容假设已在服务器添加dbox用户用于dropbox服务的同步处理。并且也已经设置了与dropbox账户的关联。

启动dropboxd

用dbox用户登录服务器。

然后,启动dropboxd进程。

~/dropbox-dist/dropboxd &

设置文件夹监测

先安装incron服务。
apt-get install incron
yum install incron
开机启动

安装sysv-rc-conf,用于管理服务的启动

apt-get install sysv-rc-conf
sysv-rc-conf incron on
sysv-rc-conf --list //用于查看所有服务的状态

创建监测服务

先修改下incron的编辑器

sudo vi /etc/incron.conf

在文件的最后一行,去掉editor = vi前的#,保存退出。

输入:

incrontab -e

如果当前登录的不是dbox用户,可以使用

incrontab -udbox -e

然后输入:

/home/dbox/Dropbox/yourfolder/ IN_ATTRIB,IN_MOVE /home/dbox/dbox.sh

第一个参数:用来接收Dropbox同步的文件夹

第二个参数:指监测的动作

第三个参数:处理脚本

监测的动作可以用:

IN_ACCESS,即文件被访问
IN_MODIFY,文件被 write
IN_ATTRIB,文件属性被修改,如 chmod、chown、touch 等
IN_CLOSE_WRITE,可写文件被 close
IN_CLOSE_NOWRITE,不可写文件被 close
IN_OPEN,文件被 open
IN_MOVED_FROM,文件被移走,如 mv
IN_MOVED_TO,文件被移来,如 mv、cp
IN_CREATE,创建新文件
IN_DELETE,文件被删除,如 rm
IN_DELETE_SELF,自删除,即一个可执行文件在执行时删除自己
IN_MOVE_SELF,自移动,即一个可执行文件在执行时移动自己
IN_UNMOUNT,宿主文件系统被 umount
IN_CLOSE,文件被关闭,等同于(IN_CLOSE_WRITE | IN_CLOSE_NOWRITE)
IN_MOVE,文件被移动,等同于(IN_MOVED_FROM | IN_MOVED_TO)
#上面所说的文件也包括目录。
处理同步后的文件的脚本
cd /home/dbox/
vi dbox.sh

cd /home/dbox/Dropbox/yoursite/
cp -R public/ /var/www/yoursite/

最后一句要注意看你本地同步了哪些内容,还要注意与网站的目录对应。

还要注意dbox.sh要有执行权限,和yoursite的写入权限。

至此,完成。

使用Let's Encrypt制作数字证书

Let’s Encrypt是一个新的CA机构,提供非常简单并且免费的TLS/SSL证书服务。

可谓万众期待。

说下我的环境:

Ubuntu 14.04.3
Nginx 1.4.6

首先你要有个域名,其次要有个服务器。

STEP 0 - 安装Let’s Encrypt 客户端

安装git

先更新下系统

sudo apt-get update
sudo apt-get -y install git
Clone Let’s Encrypt
sudo git clone https://github.com/letsencrypt/letsencrypt /opt/letsencrypt

这里把下载的文件放到了/opt/letsencrypt文件夹

STEP 1-生成证书

首先需要关闭80端口

sudo nginx -s stop

接下来运行Let’s Encrypt

cd /opt/letsencrypt
./letsencrypt-auto certonly --standalone

注意:需要超级用户权限,所以需要输入密码

之后会出现提示框,需要输入邮箱,用于接收提醒和,如果不幸丢失了Key,找回的时候也需要邮箱。

根据提示进行操作即可,接下来啥同意用户协议。

下一步则是输入你需要生成证书的域名了,如果需要一个证书给多个域名使用,这些域名则要全部输入,(example.com和www.example.com是不同的域名),域名之间用空格,逗号,左斜杠 分隔。

成功后会有如下提示:

IMPORTANT NOTES:

 - If you lose your account credentials, you can recover through
   e-mails sent to sammy@digitalocean.com
 - Congratulations! Your certificate and chain have been saved at
    /etc/letsencrypt/live/example.com/fullchain.pem. Your
   cert will expire on 2016-03-15. To obtain a new version of the
   certificate in the future, simply run Let's Encrypt again.
 - Your account credentials have been saved in your Let's Encrypt
   configuration directory at /etc/letsencrypt. You should make a
   secure backup of this folder now. This configuration directory will
   also contain certificates and private keys obtained by Let's
   Encrypt so making regular backups of this folder is ideal.
 - If like Let's Encrypt, please consider supporting our work by:

   Donating to ISRG / Let's Encrypt:   https://letsencrypt.org/donate
   Donating to EFF:                    https://eff.org/donate-le

证书位置:/etc/letsencrypt/live/example.com/fullchain.pem

过期时间:2016-03-15

至于过期时间为什么这么短则是出于安全考虑。不宜使用同一个证书太长时间。

证书文件:

上一步成功后,会有如下4个文件生成。

sudo ls /etc/letsencrypt/live/your_domain_name
cert.pem: 证书
chain.pem: The Let's Encrypt chain certificate(不知道什么鬼)
fullchain.pem: cert.pem and chain.pem combined
privkey.pem: 证书私钥

STEP 2-配置Web服务器(Nginx)

证书生成完毕,现在可以配置Web服务器了。

编辑配置文件:

sudo vi /etc/nginx/sites-available/default

在server区块中,增加如下代码:

listen 443 ssl;
server_name example.com;
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;

如果之前是绑定的80端口,直接改为443即可,后边增加 ssl 。

如果想使用最安全的SSL协议,增加如下代码:

ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_prefer_server_ciphers on;
ssl_ciphers AES256+EECDH:AES256+EDH:!aNULL;

最后,在server区块后再增加一段,用于从HTTP跳转到HTTPS。

server {
    listen 80;
    server_name example.com;
    rewrite ^/(.*) https://example.com/$1 permanent;
}

保存,退出。

重启Nginx服务器。

sudo nginx -s reopen

现在已经可以通过HTTPS访问网站了。

原文中还有设置自动生成证书的部分,出于懒的原因此处不再写。

参考资料: How To Secure Nginx with Let’s Encrypt on Ubuntu 14.04

PHP使用Redis作为缓存使用PDO读取MySQL数据

图片转自:Using PDO::fetchAll – Examples with codes and output results

一个简单的例子。

实现功能:

1.使用PDO读取数据

2.使用Redis缓存结果

3.再次查询时会从Redis查询,减少MySQL查询

没有注意代码的逻辑,仅实现思路。

<?php

//一上来肯定是配置pdo的连接信息。其实这步在这个示例中可以放到第一个else中。
$dsn = 'mysql:dbname=node;host=127.0.0.1';
$user = 'root';
$password = '';

//连接Redis
$redis= new Redis();
$redis->connect('127.0.0.1',6379);

//接收查询参数
$id = intval($_GET['id']);

//设置在Redis中存储的KEY
$MY_NODE_KEY_ = 'TEST_PDO_REDIS_ID_';

//拼接KEY和查询ID,读取Redis,
$cache = $redis->get($MY_NODE_KEY_.$id);

//用来插入log的时间参数
$date = date("Y-m-d H:i:s");
if($cache){
	//Redis缓存存在则直接输出
	print_r(json_decode($cache,true));
	//并记录log
	error_log($date."---read from redis \r\n", 3, './debug.txt');
}else{
	//缓存不存在,则连接PDO
	try {
    	$pdo = new PDO($dsn, $user, $password);
    	error_log($date."---Connection Succcess \r\n", 3, './debug.txt');
		
		//查询
	    $query = $pdo -> query("select * from news where id = '$id'");
		//设置结果集为数组
	    // [PDOStatement::fetch](http://php.net/manual/zh/pdostatement.fetch.php)
	    $query->setFetchMode(PDO::FETCH_ASSOC);
	    
	    $rs = $query->fetch();
	
	    if (is_array($rs)) {
	    	//查询完成,以json格式写入Redis中。
			$redis->set($MY_NODE_KEY_.$rs['id'],json_encode($rs));
			print_r($rs);
			error_log($date."read from mysql \r\n", 3, './debug.txt');
	    }
	    
	//PDO连接出错
	} catch (PDOException $e) {
		//输出错误信息,并记录log中
    	echo 'Connection failed: ' . $e->getMessage();
    	error_log($date."---Connection failed \r\n", 3, './debug.txt');
	}
}

写的很糙,逻辑已经简单到没有逻辑了。

一句话说一下思路就是: 先看有没有缓存 木有就查数据库,写入缓存

参考资料: PHP 数据对象 PHP PDO的简单使用(query(),exec(),prepare(),Transaction,行锁)

Node.js连接MySQL并读取数据

文章部分代码与之前整理的一篇文章一样。

本文主要实现node连接MySQL并读取指定表的数据输出到ejs模板中。

MySQL是一款非常常用的开源数据库,npm中也有MySQL的包

###MySQL测试库的表结构:

DROP TABLE IF EXISTS `news`;
CREATE TABLE `news` (
  `id` int(11) NOT NULL DEFAULT '0',
  `title` varchar(45) NOT NULL DEFAULT '',
  `createtime` int(11) NOT NULL DEFAULT '0',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

LOCK TABLES `news` WRITE;
INSERT INTO `news` VALUES (1,'news one',1449064787),(2,'news two',1449064790),(3,'news three',1449064900);
UNLOCK TABLES;

安装Node.js MySQL包

npm install mysql

server.js

//server.js
var express = require('express');
var mysql = require('mysql');
var moment = require('moment');//用来格式化UNIX时间戳
var app = express();

配置MySQL数据库

var connection = mysql.createConnection({
	host:'localhost',
	user:'root',
	password:'root',
	database:'node'
});

连接数据库,并在成功或失败时输出log

connection.connect(function(err){
	if(!err){
		console.log('Database is connected...\n\n');
	}else{
		console.log('Error Connecting Database...\n\n');
	}
});

定义表

var TABLE = 'news';

设定模板引擎

app.set('view engine','ejs');

这就是很普通的路由,表示接收访问首页的请求

app.get('/',function(req,res){
	//定义一组数据
	var data = [
					{ name : 'Bloody Mary' , drunkness : 3 },
					{ name : 'Martini' , drunkness : 5 },
					{ name : 'Scotch' , drunkness : 10 }
				];

	//MySQL查询
	connection.query(
		//普通的SQL
		'select * from ' + TABLE,
		//查询回调
		function(err,results,fields){
			if(err){
				//输出错误
				throw err;
			}
			
			if(results){
				console.log(results);
				//express render一个页面
				res.render('pages/index',{
					title:'test',
					results:results,
					data:data,
					moment:moment
				});
			}
		}
	)

});

app.listen(8888);
console.log('8888 is the magic port');

index.ejs

<% include ../partials/head %>

<body class="container">
	<main>
		<div class="jumbotron">
			<h2>data from static</h2>
			<ul>
				<% data.forEach(function(d) { %>
				<li>
					<%= d.name %>
					<span><%= d.drunkness %></span>
				</li>
			    <% }); %>
			</ul>
		</div>
		<div class="jumbotron">
			<h2>data from mysql</h2>
			<ul>
				<% results.forEach(function(result) { %>
				<li>
					<%= result.id %>
					<span><%= result.title %></span>
					<span><%= moment(result.createtime*1000).format('YYYY-MM-DD, hh:mm:ss') %></span>
				</li>
			    <% }); %>
			</ul>
		</div>
		
	</main>

	<footer>
		<% include ../partials/footer %>
	</footer>
	
</body>
</html>

至此,就可以正常输出从数据库里读取出来的数据了。

小插曲

之前在练习的时候,require MySQL 之后npm install 只安装了require的几个包,昨天再写的时候装了一大堆。 另外之前在使用moment的时候并没有在render页面的时候传入moment,直接就可以用了,昨天也报错了。

MySQL命令行导出结果集到Excel

某个需求,为了简单单独建表存储,而且没有相关的后台管理方法,无法查看,或导出,但是又突然出现需要导出这个需求。

所以突然想到MySQL是否带了这种直接导出到文件中的方法,查资料后发现果然有。

只需要在命令行中执行如下查询即可。

SELECT * FROM my_table INTO OUTFILE 'file.csv' FIELDS TERMINATED BY ',';

有几点需要注意:

0.文件将在服务器上生成,所以需要需要MySQL进程有生成文件的权限。
1.必须为一个不存在的文件名。

其实还有一些功能,或者说是说明,这里并没有写。

官方文档:

13.2.9.1 SELECT … INTO Syntax

JS标识符

标识符由一个字母开头,其后可以选择性的加上一个或多个字母、数字或下划线。但是,不能使用下面这些保留字:

abstract
boolean break byte
case catch char class const continue
debugger default delete do double
else enum export extends
false final finally float for function
goto
if implements import in instanceof int interface 
long
native new null
package private protected public
return
short static super switch synchronized
this throw throws transient true try typeof
var volatile void
while with

这个列表里的大部分并未用在JS语言里,有一些已经逐渐在新标准中出现,但是这个列表也不包括一些本应该保留的字,如undefined,NaN,Infinity。

JS不允许使用保留字来命名变量或参数,也不允许在对象字面量中,或者用 点运算符(.)提取对象属性时,使用保留字作为对象的属性名。

标识符被用于语句,变量,参数,属性名,运算符和标记。

注:实际中,JS也允许 (_),($)开头。

利用Dropbox同步hexo的文章源文件并自动生成文章发布

** 阅读之前默认为已在服务器安装hexo,也默认服务器可以访问Dropbox **

** 不建议服务器内存小于512M ,谁卡谁知道 **

最初的最初

先说下我最终完成的结构:

1.本地有一个日志文件夹,用于存放md文件,或HTML文件,作为本地的Dropbox客户端同步的目录
2.服务器home文件夹中init一套hexo程序,用于接收本地的md文件,和generate,minify

流程:

1.本地撰写,本地客户端自动同步
2.服务器Dropbox设置的同步目录接收本地的内容并cp到专门用于生成的hexo目录里
3.服务器用于生成的hexo生成新日志,cp到web目录中

这么做的原因是,如果一共(是整个博客一共)只有一篇文章或几篇文章,那几乎没影响,如果,有几十篇了,据我观察,生成很耗时,可能会导致搜索引擎访问出现404,用户打开一个生成到一半的文件,用户打开一个木有样式的文章。

STEP 0 安装Dropbox

注册Dropbox

if( ! has_dropbox_account() ){
	return "请先注册个账户啊亲";
}

由于Dropbox默认安装在~,所以建议新建一个专用于同步的账户,如dbox。

adduser dbox
passwd dbox 

** 请记住密码。请记住密码。请记住密码。 **

然后,用新建的账户登录进去。

终于可以安装Dropbox了。

这里要区分一下你的系统,

** 32位: **

cd ~ && wget -O - "https://www.dropbox.com/download?plat=lnx.x86" | tar xzf -

** 64位: **

cd ~ && wget -O - "https://www.dropbox.com/download?plat=lnx.x86_64" | tar xzf -

然后就可以运行Dropbox的守护程序了

~/.dropbox-dist/dropboxd &

第一次在新的电脑上启动,会提示:

此电脑尚未与任何 Dropbox 帐户关联...

请访问 https://www.dropbox.com/cli_link_nonce?nonce=95cd317d2***** 来关联此设备。

此电脑现在已与 Dropbox 关联。欢迎 yourname username

** 注意在出现“此电脑现在已与 Dropbox 关联。欢迎 your username”前不要ctrl+c 退出这个程序。 **

打开授权链接后会出现如下的提示,选择连接即可。

一定要注意,不然你都会奇怪为啥再次启动这个进程的时候还会出现“尚未关联的提示”

验证成功后,会在当前用户的home目录中创建Dropbox目录,即Dropbox同步的目录。

此时,如果你打开了 top,那应该发现此时服务器的内存有点捉急了,原因是Dropbox这个进程占的很多,所以一般情况下killall dropbox 退出就好了,壕请随意。

STEP 1 设置文件夹监测

安装incron服务

incron是Linux下一个监测文件变化的服务

apt-get install incron
yum install incron

** 设置开机启动 **

安装sysv-rc-conf,用于管理服务的启动

apt-get install sysv-rc-conf
sysv-rc-conf incron on

** 创建监测任务 **

先修改下incron的编辑器

vi /etc/incron.conf

(此时可能需要sudo权限,因为是dbox用户)在文件的最后一行,去掉editor = vi前的#,保存退出。

输入:incrontab -e

/home/dbox/Dropbox/yourfolder/ IN_ATTRIB,IN_MOVE /home/dbox/hexo.bash

第一个参数:用来接收Dropbox同步的文件夹

第二个参数:指监测的动作

第三个参数:处理脚本

监测的动作可以用:

IN_ACCESS,即文件被访问
IN_MODIFY,文件被 write
IN_ATTRIB,文件属性被修改,如 chmod、chown、touch 等
IN_CLOSE_WRITE,可写文件被 close
IN_CLOSE_NOWRITE,不可写文件被 close
IN_OPEN,文件被 open
IN_MOVED_FROM,文件被移走,如 mv
IN_MOVED_TO,文件被移来,如 mv、cp
IN_CREATE,创建新文件
IN_DELETE,文件被删除,如 rm
IN_DELETE_SELF,自删除,即一个可执行文件在执行时删除自己
IN_MOVE_SELF,自移动,即一个可执行文件在执行时移动自己
IN_UNMOUNT,宿主文件系统被 umount
IN_CLOSE,文件被关闭,等同于(IN_CLOSE_WRITE | IN_CLOSE_NOWRITE)
IN_MOVE,文件被移动,等同于(IN_MOVED_FROM | IN_MOVED_TO)
#上面所说的文件也包括目录。

可以选自己需要的动作

接下来写处理脚本

vi hexo.bash
cp -R /home/dbox/Dropbox/yourfolder/* /home/dbox/dropasync/source/_posts/;
cd /home/dbox/dropasync/ && hexo gm
cd ~ && ./.dropbox-dist/dropboxd

理论上到这里已经可以了。

但是实际上我本人在测试的时候,512内存的服务器会只剩下4M内存,然后再执行任何命令都提示 -bash: fork: Cannot allocate memory,持续很长时间,一开始以为是同时跑的进程太多导致内存不够,把没用的mongodb,redis都kill之后还是这样,后来干脆在执行这个脚本的时候把Dropbox的后台禁掉,也不行,之后想到了在这段脚本里echo一段数字到log中,执行的时候发现会写入多次,但是还是不清楚为什么会出现这种情况,进程里也是出现了两个或多个hexo,于是干脆就在这段脚本开始时先kill dropbox和hexo,在末尾再kill hexo。实际运行起来,内存还是会降到最低,但是持续时间明显减少到可以接受的程度。


2015-11-21 14:06:07 附截图


2015-11-14 22:38:06 测试发现,上述带删除线的方法依然不行。内存依然会榨干很长时间。


至于为何出现这个情况,咱不可知。。。总不能完全是因为内存太小吧……

后续应该还会继续改进这个脚本。

另外,在安装这个用于生成日志的hexo程序的时候也出现了npm killed的问题,查了资料发现也是因为内存不足,npm install -d 可以查看安装过程,如果出错可以用来定位到底是什么原因引起错误

一些参考资料:

Hexo 服务器端布署及 Dropbox 同步

用Hexo+Vps搭建博客并用Dropbox同步自动发布

incrontab(5) - Linux man page

incron:linux下基于文件的事件触发

Why is chkconfig no longer available in Ubuntu?

Use incron to Trigger Action when File Changes 如果设置incrontab出现问题可以参考这篇

为hexo加一个时钟小挂件

之前在 HTML+CSS3再加一点点JS做的一个小时钟 看到的这个问题,觉得很好把代码存下来了,今天突发奇想把它放到hexo的新博客上。

STEP 0

把HTML内容放到新建的模板里,我命名为time.ejs。

	<div class="widget-wrap">
	<h3 class="widget-title">Time</h3>
	<div class="widget time">
		<style id="clock-animations"></style>
		<div class="clock-wrapper">
			<div class="clock-base">
					<div class="clock-indicator">
						<div></div>
						<div></div>
						<div></div>
						<div></div>
						<div></div>
						<div></div>
						<div></div>
						<div></div>
						<div></div>
						<div></div>
						<div></div>
						<div></div>
					</div>
					<div class="clock-hour"></div>
					<div class="clock-minute"></div>
					<div class="clock-second"></div>
					<div class="clock-center"></div>
			</div>
		</div>
	</div>
</div>

原文中<style id="clock-animations"></style>是放在head区域的,为了减小对全局的影响,我拿到这个挂件中了,实际上并没有影响。

这里要注意挂件的代码结构要尽量与主题自带的结构一致。(仅仅是为了好看,和好整理)

当然了,这个文件要放在layout/_widget下。

STEP 1

js脚本

由于脚本内容较少,我就直接放在了script.js中,文件位置再source/js。

var now = new Date();

var second = now.getSeconds(); var minute = now.getMinutes(); var hour = now.getHours(); if (hour > 12) { hour = hour - 12; } hourDeg = hour * 30 + now.getMinutes() / 60 * 30; minuteDeg = now.getMinutes() * 6; secondDeg = now.getSeconds() * 6; stylesDeg = [ “@keyframes rotate-hour{ from{transform:rotate(” + hourDeg + “deg);}to{transform:rotate(” + (hourDeg + 360) + “deg);}}”, “@keyframes rotate-minute{from{transform:rotate(” + minuteDeg + “deg);}to{transform:rotate(” + (minuteDeg + 360) + “deg);}}”, “@keyframes rotate-second{from{transform:rotate(” + secondDeg + “deg);}to{transform:rotate(” + (secondDeg + 360) + “deg);}}”, “@-moz-keyframes rotate-hour{ from{transform:rotate(” + hourDeg + “deg);}to{transform:rotate(” + (hourDeg + 360) + “deg);}}”, “@-moz-keyframes rotate-minute{from{transform:rotate(” + minuteDeg + “deg);}to{transform:rotate(” + (minuteDeg + 360) + “deg);}}”, “@-moz-keyframes rotate-second{from{transform:rotate(” + secondDeg + “deg);}to{transform:rotate(” + (secondDeg + 360) + “deg);}}”, “@-webkit-keyframes rotate-hour{from{transform:rotate(” + hourDeg + “deg);}to{transform:rotate(” + (hourDeg + 360) + “deg);}}”, “@-webkit-keyframes rotate-minute{from{transform:rotate(” + minuteDeg + “deg);}to{transform:rotate(” + (minuteDeg + 360) + “deg);}}”, “@-webkit-keyframes rotate-second{from{transform:rotate(” + secondDeg + “deg);}to{transform:rotate(” + (secondDeg + 360) + “deg);}}” ].join(“”); $(‘#clock-animations’).html(stylesDeg);

STEP 2

CSS样式

新建文件time.styl,路径为/source/css/_partial/time.styl

内容略多,主要是给多浏览器写前缀。

.clock-wrapper {
	position: relative;
	height: 250px;
	width: 250px;
	background-image: linear-gradient(#f7f7f7,#e0e0e0);
	border-radius: 50%;
	box-shadow: 0 10px 15px rgba(0,0,0,.15),0 2px 2px rgba(0,0,0,.2);
}
.clock-base {
	width: 250px;
	height: 250px;
	background-color: #eee;
	border-radius: 50%;
	box-shadow: 0 0 5px #eee;
}
.clock-indicator {
	z-index: 1;
	position: absolute;
	top: 15px;
	left: 15px;
	width: 230px;
	height: 230px;
}
.clock-indicator div {
	position: absolute;
	width: 2px;
	height: 4px;
	margin: 113px 114px;
	background-color: #a4a4a4;
}
.clock-indicator div:nth-child(1) {
	transform:rotate(30deg) translateY(-113px);
	-ms-transform:rotate(30deg) translateY(-113px);     /* IE 9 */
	-moz-transform:rotate(30deg) translateY(-113px);    /* Firefox */
	-webkit-transform:rotate(30deg) translateY(-113px); /* Safari 和 Chrome */
	-o-transform:rotate(30deg) translateY(-113px);  /* Opera */        }
	.clock-indicator div:nth-child(2) {
	transform:rotate(60deg) translateY(-113px);
	-ms-transform:rotate(60deg) translateY(-113px);     /* IE 9 */
	-moz-transform:rotate(60deg) translateY(-113px);    /* Firefox */
	-webkit-transform:rotate(60deg) translateY(-113px); /* Safari 和 Chrome */
	-o-transform:rotate(60deg) translateY(-113px);  /* Opera */
}
.clock-indicator div:nth-child(3) {
	transform:rotate(90deg) translateY(-113px);
	-ms-transform:rotate(90deg) translateY(-113px);     /* IE 9 */
	-moz-transform:rotate(90deg) translateY(-113px);    /* Firefox */
	-webkit-transform:rotate(90deg) translateY(-113px); /* Safari 和 Chrome */
	-o-transform:rotate(90deg) translateY(-113px);  /* Opera */
	background-color: #5a5a5a;
}
.clock-indicator div:nth-child(4) {
	transform:rotate(120deg) translateY(-113px);
	-ms-transform:rotate(120deg) translateY(-113px);    /* IE 9 */
	-moz-transform:rotate(120deg) translateY(-113px);   /* Firefox */
	-webkit-transform:rotate(120deg) translateY(-113px); /* Safari 和 Chrome */
	-o-transform:rotate(120deg) translateY(-113px);     /* Opera */
}
.clock-indicator div:nth-child(5) {
	transform:rotate(150deg) translateY(-113px);
	-ms-transform:rotate(150deg) translateY(-113px);    /* IE 9 */
	-moz-transform:rotate(150deg) translateY(-113px);   /* Firefox */
	-webkit-transform:rotate(150deg) translateY(-113px); /* Safari 和 Chrome */
	-o-transform:rotate(150deg) translateY(-113px);     /* Opera */
}
.clock-indicator div:nth-child(6) {
	transform:rotate(180deg) translateY(-113px);
	-ms-transform:rotate(180deg) translateY(-113px);    /* IE 9 */
	-moz-transform:rotate(180deg) translateY(-113px);   /* Firefox */
	-webkit-transform:rotate(180deg) translateY(-113px); /* Safari 和 Chrome */
	-o-transform:rotate(180deg) translateY(-113px);     /* Opera */
	background-color: #5a5a5a;
}
.clock-indicator div:nth-child(7) {
	transform:rotate(210deg) translateY(-113px);
	-ms-transform:rotate(210deg) translateY(-113px);    /* IE 9 */
	-moz-transform:rotate(210deg) translateY(-113px);   /* Firefox */
	-webkit-transform:rotate(210deg) translateY(-113px); /* Safari 和 Chrome */
	-o-transform:rotate(210deg) translateY(-113px);     /* Opera */
}
.clock-indicator div:nth-child(8) {
	transform:rotate(240deg) translateY(-113px);
	-ms-transform:rotate(240deg) translateY(-113px);    /* IE 9 */
	-moz-transform:rotate(240deg) translateY(-113px);   /* Firefox */
	-webkit-transform:rotate(240deg) translateY(-113px); /* Safari 和 Chrome */
	-o-transform:rotate(240deg) translateY(-113px);     /* Opera */
}
.clock-indicator div:nth-child(9) {
	transform:rotate(270deg) translateY(-113px);
	-ms-transform:rotate(270deg) translateY(-113px);    /* IE 9 */
	-moz-transform:rotate(270deg) translateY(-113px);   /* Firefox */
	-webkit-transform:rotate(270deg) translateY(-113px); /* Safari 和 Chrome */
	-o-transform:rotate(270deg) translateY(-113px);     /* Opera */
	background-color: #5a5a5a;
}
.clock-indicator div:nth-child(10) {
	transform:rotate(300deg) translateY(-113px);
	-ms-transform:rotate(300deg) translateY(-113px);    /* IE 9 */
	-moz-transform:rotate(300deg) translateY(-113px);   /* Firefox */
	-webkit-transform:rotate(300deg) translateY(-113px); /* Safari 和 Chrome */
	-o-transform:rotate(300deg) translateY(-113px);     /* Opera */
}
.clock-indicator div:nth-child(11) {
	transform:rotate(330deg) translateY(-113px);
	-ms-transform:rotate(330deg) translateY(-113px);    /* IE 9 */
	-moz-transform:rotate(330deg) translateY(-113px);   /* Firefox */
	-webkit-transform:rotate(330deg) translateY(-113px); /* Safari 和 Chrome */
	-o-transform:rotate(330deg) translateY(-113px);     /* Opera */
}
.clock-indicator div:nth-child(12) {
	transform:rotate(360deg) translateY(-113px);
	-ms-transform:rotate(360deg) translateY(-113px);    /* IE 9 */
	-moz-transform:rotate(360deg) translateY(-113px);   /* Firefox */
	-webkit-transform:rotate(360deg) translateY(-113px); /* Safari 和 Chrome */
	-o-transform:rotate(360deg) translateY(-113px);     /* Opera */
	background-color: #5a5a5a;
}
.clock-hour {
	z-index: 2;
	position: absolute;
	left: 128px;
	top: 80px;
	width: 4px;
	height: 65px;
	border-radius: 2px;
	background-color: #555;
	box-shadow: 0 0 2px rgba(0,0,0,0.2); 
	transform-origin: 2px 50px;
	-moz-transform-origin: 2px 50px;
	-ms-transform-origin: 2px 50px;
	-o-transform-origin: 2px 50px;
	-webkit-transform-origin: 2px 50px;
	transition: 1s;
	-moz-transition: 1s; /* Firefox 4 */
	-webkit-transition: 1s; /* Safari 和 Chrome */
	-o-transition: 1s; /* Opera */
	animation:rotate-hour 43200s linear infinite;
	-webkit-animation: rotate-hour 43200s linear infinite; /* Safari 和 Chrome */
}
.clock-minute {
	z-index: 3;
	position: absolute;
	left: 128px;
	top: 60px;
	width: 4px;
	height: 85px;
	border-radius: 2px;
	background-color: #555;
	box-shadow: 0 0 2px rgba(0,0,0,0.2); 
	transition: 1s;
	-moz-transition: 1s; /* Firefox 4 */
	-webkit-transition: 1s; /* Safari 和 Chrome */
	-o-transition: 1s; /* Opera */
	transform-origin: 2px 70px;
	-moz-transform-origin: 2px 70px;
	-ms-transform-origin: 2px 70px;
	-o-transform-origin: 2px 70px;
	-webkit-transform-origin: 2px 70px;
	animation:rotate-minute 3600s linear infinite;
	-webkit-animation: rotate-minute 3600s linear infinite; /* Safari 和 Chrome */
}
.clock-second {
	z-index: 4;
	position: absolute;
	left: 129px;
	top: 15px;
	width: 2px;
	height: 140px;
	border-radius: 2px;
	background-color: #a00;
	box-shadow: 0 0 1px rgba(0,0,0,0.2); 
	transform-origin: 1px 115px;
	-moz-transform-origin: 1px 115px;
	-ms-transform-origin: 1px 115px;
	-o-transform-origin: 1px 115px;
	-webkit-transform-origin: 1px 115px;
	transition: 1s;
	-moz-transition: 1s; /* Firefox 4 */
	-webkit-transition: 1s; /* Safari 和 Chrome */
	-o-transition: 1s; /* Opera */
	animation:rotate-hour 60s linear infinite;
	 -webkit-animation: rotate-second 60s linear infinite;  /* Safari 和 Chrome */
}
.clock-second:after {
	content: "";
	display: block;
	position: absolute;
	width: 8px;
	height: 8px;
	border-radius: 50%;
	left: -3px;
	top:110px;
	background-color: #a00;
	box-shadow: 0 0 3px rgba(0,0,0,0.2);
}
.clock-center {
	z-index: 1;
	position: absolute;
	left: 55px;
	top: 55px;
	background-image: linear-gradient(#e3e3e3,#f7f7f7); 
	border-radius: 50%;
	width: 150px;
	height: 150px;
	box-shadow: inset 0 -1px 0 #fafafa, inset 0 1px 0 #e3e3e3;
}
.clock-center:after{
	content: "";
	display: block;
	width: 20px;
	height: 20px;
	margin: 65px;
	background-color: #ddd;
	border-radius: 50%;
}

之后,在sidebar.styl 末尾加入 @import "time",这是styl的语法,表示引入一个文件,因为两个文件是同级,所以可以直接这么写,加./也可以。

STEP 3

最后,在themes的config文件中注册这个挂件。

在_config.yml中,找到widgets,在你想要加的位置中加入 - time,即模板文件名。 比如我的widgets变成了这样:

widgets:
- clock
- category
- tag
- tagcloud
- archive
- recent_posts

LAST

至此,就全部完事,可以去hexo generatehexo server了。

vps 搭建个人git服务器

以下内容主要来自 How To Set Up a Private Git Server on a VPS

之前在DigitalOcean买了个5刀的vps,本来是想搞VPN的,但是没成功,后来把个人博客放这了,后来又觉得有点浪费,索性重新启用这个域名来写技术文章。

本来是用的WP的但是也一直没写,后来又想折腾别的程序试一下,就选了现在的hexo,昨天在自己电脑上安装了,也可以写文章页可以默认 hexo server 运行,然后,我愉快的把命令行关掉之后,就傻了。

后来也在官方文档和Google里看有什么办法能让它在后台运行,官方说可以 hexo s &,然并卵,官方和Google都说可以用 forever pm2 ,一样然并卵,今天早晨起来继续弄的时候,觉得还是放弃吧,既然有public文件夹,还是用nginx去解析吧。这个都是后话了。

下边说怎么在vps上安装git服务器,昨天和今天上午也看了一些资料一直也不成功。晚上从DigitalOcean社区里看到这篇文章,然后就成功了。

0 在本地生成ssh key

ssh-keygen -C "youremail@mailprovider.com"

Generating public/private rsa key pair.

Enter file in which to save the key (/home/flynn/.ssh/id_rsa):

Enter passphrase (empty for no passphrase):

Enter same passphrase again: 

Your identification has been saved in foo_rsa.

Your public key has been saved in foo_rsa.pub.

注意替换成自己的邮箱,可以一路回车,也可以在 Enter passphrase 的时候输入一个密码保护一下。如果已经生成过了可以 略过这步

1 在服务器添加git用户

首先切换到root用户,su - 。 然后添加git用户。(用户名不一定是git,但是习惯上用这个名)

useradd git

设置密码

passwd git

输入两次密码即可,用户创建完毕。我在操作的时候并没有在/home/下创建git用户的目录。

所以可能需要自己手动创建

cd /home/

mkdir git

sudo chown -R -v git:git git/

现在可以安装git服务了。

CentOS/Fedora: yum install git
Ubuntu/Debian: apt-get install git

DO文档说可以这样,但是部分资料里写要 install git-core,但是因为之前安装过git-core,所以不确定是不是DO文档上是正确的,所以此处DO的文档可能不准确。(不确定)

2 把本地的ssh key添加到服务器的允许访问列表里

登录进服务器,切换到git用户。

su git

然后创建git用户的.ssh文件夹及authorized_keys文件,

mkdir ~/.ssh && touch ~/.ssh/authorized_keys

&&的意思是执行完第一个命令后紧接着执行第二个命令,分开写一样可以,连着写逼格更高。

然后回到本地的命令行里,

cat .ssh/id_rsa.pub | ssh git@123.45.56.78 "cat >> ~/.ssh/authorized_keys"

第一个cat 后的文件地址取决于你电脑上这个文件的路径,后边是说把前边你本地的id_rsa.pub的内容写到远程服务器上git 这个用户的.ssh/authorized_keys文件里,也就是前边创建的那个文件。

所以,不出意外的话,此时可以在服务器上运行

cat ~/.ssh/authorized_keys 就可以看到你本地电脑生成的key了,不出意外的话结尾应该是你生成key填写的邮箱。

3 初始化本地仓库

在服务器上任何一个位置,执行

git init --bare project.git,这样,在你的开发机和服务器环境是就可以用到这个project.git了。

(为已存在的git项目)设置远程仓库的URL

git remote set-url origin git@git.droplet.com:project.git

git@git.droplet.com:project.git 替换成你的git用户名,你的ip或域名,及你的服务器上git的文件夹

如果是一个新的仓库,是这样

git init && git remote add origin git@git.droplet.com:project.git

git@git.droplet.com:my-project.gi 只是服务器上

总结:

所以实际上上边的流程是

0. 在服务器上比如home/git 目录里git init --bare project.git
1. 在自己电脑,某个目录,git init 或者git clone git@git.droplet.com:project.git
2. 在服务器上比如www目录,git init 或git clone git@git.droplet.com:project.git

不出意外,这样就可以了。

本地添加一个新文件,git add . , git commit -m "test" ,git push origin master,登录服务器,切换到www目录,git pull origin master,就可以了。

参考资料:

How To Set Up a Private Git Server on a VPS

Git on the Server - Setting Up the Server

React-Native Fetch方法发送网络请求

先贴一个官方文档。 Network

fetch('https://mywebsite.com/endpoint/', {
    method: 'POST',
    headers: {
        'Accept': 'application/json',
        'Content-Type': 'application/json',
    },
    body: JSON.stringify({
        firstParam: 'yourValue',
        secondParam: 'yourOtherValue',
    })
})

一开始我就是单纯的这么写的,愉快的把URL替换掉,把参数替换掉,然后。。。 我愉快的敲下 var_dump($_POST['firstParam']) 的时候,后台没接到任何东西,后来在stackoverflow上看到回答,说之所以接收不到,是因为这不属于form data,想要这么写,需要在接收前添加 file_get_contents('php://input'),然后果然就可以了。

$json = json_decode(file_get_contents('php://input'), true);
var_dump($json['firstParam']);

但是这样会影响到现有的web应用,所以又继续查资料,发现可以改成下边这样的方法。

function toQueryString(obj) {
    return obj ? Object.keys(obj).sort().map(function (key) {
        var val = obj[key];
        if (Array.isArray(val)) {
            return val.sort().map(function (val2) {
                return encodeURIComponent(key) + '=' + encodeURIComponent(val2);
            }).join('&');
        }

        return encodeURIComponent(key) + '=' + encodeURIComponent(val);
    }).join('&') : '';
}


fetch(url, {
    method: 'post',
    body: toQueryString({ 
        'firstParam': 'yourValue',
        'secondParam':'yourOtherValue' 
    })
}) 

这样,在后台就可以正常的用$_POST[‘firstParam’];接收了。

Sending data as key-value pair using fetch polyfill in react-native

How can I use react-native with php with fetch return data is always null