折腾NAS—购买篇

之前一直想买个外置硬盘盒,选来选去都没见啥中意的。后来转投NAS,也没什么目标,随便看了看似乎都没什么动力,这事也就一直搁着了。直到前段时间研究CDN的时候无意间转到了刘晖的blog,看了一系列关于NAS的文章,兴趣又被吊起来了~

设备选择

老刘买的是Synology的DS410,tb上价格已经要3k7+了,自己想了想,也用不着这么强大的。去厂商官网上转了N圈,最先看上的是DS211+,查了下价格,tb上要3k2左右,我勒个去,继续找!经过N轮的参数比较,最终选定了DS211,性能跟DS211+相差不多,仅少了一个前置SD读卡器和eSATA接口,tb报价是2k8左右,还算能接受。相关介绍可以参见这里

接下来看硬盘,既然要放进NAS里,最好能支持7×24的。目前各大硬盘厂商均推出了针对监控系统的硬盘,支持7×24,低噪音,拿来放NAS里正好(为啥不用企业级?米钱哪……)。Seagate之前的固件门搞得人心惶惶,不考虑;WD目前主流的2T监控级硬盘是WD20EURS,作为WD20EVDS的升级版本,单碟容量由500G升级到667G,盘片也由4碟减少为3碟,缓存则由32M升级至64M,感觉还是挺不错的。因为挂在NAS下,硬盘分区是EXT4,4k扇区的问题也就无所谓了。Hitachi方面则有CinemaStar 5K2000系列,基本参数跟WD20EURS差不多,可惜目前市面上似乎找不到,最终决定,就WD20EURS啦。

NAS,肯定少不了网络设备,正好我也需要买一个新的无线路由器了,基本要求就Gigabit+802.11N,能双频(2.4G+5G)双待最好,找来找去最后找了款WRT610N,DIY外壳版,将2.4G天线移到机壳的外面使用RP-SMA接口,这样据称信号能增强15%左右。

接下来开始重头戏,DS211的海外代购。

设备购买

上面提到,DS211的tb价为2K8左右,感觉价格还是有点小贵,一时也下不了决心买。后来忽然想起可以去Amazon上看看,米国的电子产品还是很便宜的,一看果然,299刀!加上最近美刀汇率一路攀升,真是划得来啊~~~

tb代购 vs 代购网站

既然要从米国买,就得考虑东西怎么运进来的问题,在米国有亲朋好友的自然是方便了,没人的话只能老老实实找代购了。

目前海外代购主要有两种形式:

  • tb代购

tb上的米国代购一般都有自己的运输网络(或者是留学生),运费能便宜不少,关税在很多情况下也能逃掉。当然也正是由于运输网络是自己的,服务质量良莠不齐,发货速度也不稳定。

  • 代购网站

目前提供米国地址的网站有很多,例如Comgateway(OneNow)、BorderLinx、库到家、AAE等等。Comgateway和BorderLinx都使用DHL来进行货物运输,作为全球四大快递之一,服务质量还是很有保证的,只是……DHL会主动报关,所以关税基本是逃不掉的。

之前买Kindle的时候就是找的tb代购,感觉还行,就是速度实在是慢了一点。后来一直想尝试一下代购网站,BorderLinx上提供一个总费用计算器,填入货物的类型、重量和尺寸,就会给出包含关税的全部费用。按照Amazon给出的信息算了一下,大约74刀,也还凑合,至少比tb便宜,DHL Express运输,2~3天到货,加上BorderLinx目前针对American Express卡用户有运费九折的优惠,就它啦!

购物开始

Borderlinx注册,获取地址,Amazon注册,选择商品,购买,填入地址,填入信用卡相关信息,确认。一路的操作基本没有任何难度。在Amazon上填写地址时,可能会提示BorderLinx提供的地址看起来似乎有点问题,不用管它,再点一次确认即可。另外,在Amazon上可以申请Prime Membership Trail,享受一个月免费的两天送货服务,这项试用服务一个账户或一张信用卡仅可申请一次。

两天之后,货物顺利到达了BorderLinx,收到通知邮件的我兴奋滴登陆BorderLinx后台,咦?

杯具的体积重量

登陆后台一看,总费用居然是94刀,囧啊囧……仔细核对条目,发现两点与之前费用计算不相同:

  1. 货品类型之前我选择的是Compuerts – External hardware,borderlinx给出的是Electronics – Other,这两者在关税计算上还是有不少差别的;
  2. 货物重量,Amazon提供的是5pound,约合2.268kg,borderlinx给出的居然是4.3kg。

查看FAQ,第一个问题很好办,跟kf联系一下就好了,第二个估计是按照体积重量来进行计算的。

体积重量是运输行业内的一项统一收费规定。体积重量是将货物体积,利用折算公式所获得的货物重量值,其计算方法如下:

  • 体积重量计算公式=长cm*宽cm*高cm ÷ 5000
  • 将“体积重量”和实际重量相比较, 运费将根据这两者中较重的一种收费

当货物体积折算的重量大于货物实际重量,将按体积重量来计算
当货物体积折算的重量小于货物实际重量,则按实际重量来计算

估计Amazon给我的东西外面包了个大箱子,live chat了一下,果然,体积达到原来的1.5倍了。这时可以要求BorderLinx尝试重新包装以减少体积,但仔细一想,一来即使重新包装估计也减不了多少,二来要重新包装的话原来包装里的气袋等缓冲设施估计会被去掉,为了货物安全考虑,还是算了吧。遂决定,直接发货。

坑爹的客服

点击发货按钮,填入AE卡相关信息,确认,咦?支付被拒绝?再试一次,还是拒绝?联系kf问问~

  • 不靠谱kf一:您是新注册的吧?货物到达BorderLinx之后到您确认发货之间小于24h吧?是这样的,因为您是新客户,我们这边需要跟银行确认相关的信息,确认之后会在您的账户页面进行提示的。嗯?您说您的账户页面没有相关信息?抱歉,我不能够查看您的账户信息,但是确认之后肯定是会有提示的。请您在从现在开始12h至24h之后再次尝试。

于是,等啊等,期间若干次支付拒绝……

  • 不靠谱kf二:您好,我刚才查了一下,BorderLinx的支付系统目前遇到了一些问题,我们正在处理,请您在从现在开始12h至24h之后再次尝试。

于是,等啊等,期间若干次支付拒绝……

诶?怎么还是不行?脑子里浮现一个模糊的印象,AE卡的Secrity Code是印在卡正面的,赶紧把卡掏出来一看,果然!之前我都是输入卡片签名栏后三位的啊啊啊……再次尝试,支付通过了,内牛满面啊~~ :cry:

接下来就是漫长的等待了,经过5天的等待,终于拿到了一个大箱子~海外代购结束!

硬盘直接走某东,6xx拿下,路由器TB找了个熟悉的卖家,480搞定。

稍作总结

从上面的过程来看,BorderLinx的客服实在是不够靠谱,估计是外包给印度之类的。不过,其他方面来说还是挺不错的。当然关税逃不掉这是选择DHL的必然,也没啥好说的。这次代购主要是在体积重量上吃了点亏,不过相比刘tx遇到的那个超级“厚道”的卖家,Amazon的包装还真算不上什么了。

这一路下来到手价为299+89=388刀,换算成RMB大约2k5左右,比tb上还是少了300,也算没亏啥。

前几天特意咨询了一下Synology关于全球联保的问题,遗憾的是Synology目前还不支持,DS211你可要争气啊~~~

总体上来说这次海外代购的经历还算是不错的,DHL较为高效的通关能力让我的东西只在海关呆了两天(当然说不定是因为五一放假),除了那些不靠谱的客服以外,其他的注册、下单、支付都还算顺利,阿弥陀佛~

唧唧歪歪

话说很长时间都没有更新blog了啊……其实吧,有了twitter之后确实没啥动力来写博了,几句话就能说明白的事情用Blog的形式太费劲,也太浪费。这次折腾NAS倒是有些东西可以拿来分享分享,希望不要是个坑才好,嗯~~

支持OAuth的Twip

Twitter官方宣布将在6月30日终止对Basic Authorization验证方式的支持,广大使用API的天朝网民纷纷表示鸭梨很大~目前最新版的3.0版Twip还不支持OAuth(2.2版支持),前几天问了下yegle童鞋,最近似乎都没有更新计划。正好最近不算太忙,一时手痒,经过一星期的“艰苦奋战”,终于让Twip 3.0支持了OAuth,命名为Twip 3.1~~原本前几天就打算发出来的,这几天不断有改进的想法冒出来,本着“精益求精”的想法,拖到了今天…… :!:

系统要求

  1. PHP版本大于5.2.4并已启用curl
  2. 若采用Apache需支持mod_rewrite
  3. mcrypt库(用于加密access token,没有也能工作)

配置方法

猛击这里下载源代码。另外,也可以使用svn checkout这个地址:http://twip.googlecode.com/svn/branches/heybronco/。基本的配置方法可参见yegle写的wiki。这里主要说一下OAuth的部分。

  1. 使用OAuth验证发推时将不显示连接到twip的客户端,而是显示与twip中OAuth CONSUMER_KEY和CONSUMER_SECRET对应的注册程序。源代码里已经包含可用的CONSUMER_KEY和CONSUMER_SECRET,对应的程序名称为”Development”,这个是编写”TwitterOAuth Library”的Abraham Williams放在示例代码中的(已经失效)。若希望使用自定义的程序名,请前往Twitter程序中心注册一个新程序,注册的时候Application Type选”Browser”,Callback URL填入填写能访问到”callback.php”文件的完整网址,Default Access type选”Read & Write”。注册完成后将”CONSUMER_KEY”和”CONSUMER_SECRET”填入”oauth_config.php”中。
  2. “oauth_config.php”为OAuth相关配置的文件,其中OAUTH_DIR指定保存OAuth token文件的位置,请放在一个web用户无法访问的目录中,比如public_html(www)目录的上一级,注意权限问题,同时确保以”/”结尾CONSUMER_KEYCONSUMER_SECRET使用默认或者替换成自己的;OAUTH_CALLBACK中填写能访问到”callback.php”文件的完整网址;SECURE_KEY中保存的是加密数据所用的随机字符,可以随便填入字符,或者保持默认。
  3. 检查index.php中”enable_oauth”项已经为true以启用OAuth,配置完成。

使用方法

访问twip的首页,页面将显示当前Twip所使用的身份验证方法等运行状态及操作说明。注意,若twip的架设者启用了”PRIVATE_API”选项,首页上将给出提示,此时需要确保自己处于被允许的用户范围,否则即使获得了access token也无法使用此API。另外,access token是否被加密存储也会显示在首页上。

获得access token需要翻墙。点击”Sign in with Twitter”按钮,进入Twitter登陆界面,输入自己的用户名和密码后,转到授权界面,询问是否授予相应权限给应用程序,点击”Access”按钮,进入保存token界面,此时要求输入密码。这个密码是用于访问保存在twip服务器上的access token文件的密码,所有通过此twip进行的发推等操作均需要使用此密码,这里称为API密码。API密码可以与自己的Twitter密码相同或不同,但若要使用Mixero,请填写Twitter密码。因为Mixero不通过API来校验密码,若API密码与Twitter不同,要么无法登陆,要么无法获得Twitter信息。完成后点击”保存”按钮,页面跳转到首页。若能看到你的timeline,说明access token获得成功。之后就可以在客户端使用用户名和API密码登陆了。

如果忘记了自己的API密码,可以点击”Sign in with Twitter”按钮来重新获得access token,原来的记录将被覆盖。若不想再继续使用此API,可进入自己的Twitter设置界面,在”connections”页面中找到此API,然后点击”Revoke Access”来移除访问许可。当然,也可以直接把twip中保存的access token删除,方法是在首页上点击”login”,输入用户名和API密码后,点击”remove the token”即可。

使用Gravity等支持OAuth的客户端需要注意一下,在添加账户的时候验证类型要选Plaintext而不是Oauth/Secure,同时账户密码填入的是API密码。Update:用户名不能填email,否则会验证失败。(才知道原来能用email登陆Gravity,thanks to mxwu)

对于想使用byethost主机的筒子们屏蔽了twitter的主机,很遗憾,使用ip替换是没办法使用OAuth的。因为在请求token的时候若使用ip地址来访问,Twitter会返回一个301,重定向到twitter.com。若点击”Sign in with Twitter”之后出现”Could not connect to Twitter. Refresh the page or try again later.”,最大的可能是主机屏蔽了Twitter,目前除了换主机以外没有解决办法。当然,敝人水平有限,也许有解决办法也说不定,这个就等待高人来解决了。 :cool: 另一种可能是key和secret失效,这个可以通过查看Debug info的http_code来确定。若为401,则表示key和secret已经失效。

安全问题

之前在推上看到有人质疑兽兽@showfom的饭否会有安全性问题,这边也会着重考虑这个问题。

若空间支持mcrypt库,access token会被加密。是否加密会在首页显示,你可以据此决定是否使用此API。access token以API密码和SECURE_KEY作为密钥进行blowfish加密。API密码将进行MD5计算,结果拼接SECURE_KEY后进行第二次MD5计算,计算结果和access token的密文以文件的形式进行保存。这样,即使是twip的拥有者也无法使用access token。若空间不支持mcrypt库(这个基本很少吧),access token将不会加密。

当然,因为twip是完全开源的,谁都可以来修改代码,上面说的安全性不一定能得到保证。至于用不用,看你自己吧。

版权

Twip原版使用Mozilla Public License 1.1授权,TwitterOAuth库虽没写明授权协议,但也有相关授权说明。若需要进行再次开发以及发布,请一定遵从相关协议的说明。

其他

感谢党,感谢国家,感谢@yegle@xmxsuperstar,感谢Abraham Williams,感谢Dreamhost。

Twip原版的相关信息可参考其Project Website。TwitterOAuth库相关信息参考这里

ps一句,本来打算找个免费空间来演示的,可惜找了几个都没搞定,哎~~~大家就自己慢慢试吧~ :smile:

以上!

Twitter-Feed的一些改进

月光写的基于gae的Twitter同步程序Twitter-feed是个很不错的程序,能将twitter上的信息同步发布到各大围脖上,通过follow5和hellotxt,甚至可以支持所有的围脖。在使用过程中,我遇到了一些小问题,最近也不算太忙,遂动手修改了一些Twitter-feed的代码。自封Twitter-feed++,嘿嘿~~

让转义字符可以同步

如果Twitter上的信息包含html元素,比如”<”等,在同步到其他围脖后,将变成转义字符,例如之前的”<”将变成”&amp; lt;”等等。仔细看了一下twitter.py,发现有个unescape函数,用来将转义字符转换为对应的符号。既然发送到其他围脖的信息还包含转义字符,很简单,再unescape一下不就好了?以R26版为例,打开twitter.py文件,在parseTwitter函数中找到text=x[1]这一行(第263行),将其改为text=unescape(x[1]),保存并update,搞定~

ps一句,我查看了一下unescape这个函数原作者的网页,发现其版本跟月光的不一样,估计月光进行了一定的修改。只是在下实在愚钝,楞没看出来为什么要那样修改。呃…… :shock:

异步进行数据发送

在parseTwitter函数中,各围脖是顺序进行同步的,同时各同步程序中没有异常处理。这样的话如果某个围脖出了问题,处于此围脖同步语句下面的其他围脖将无法进行同步。为此,可以为同步函数加上异常处理,使用”try:…except:”的语句块。另外,为了提高同步效率,可以将同步改为异步方式,参考Google的文档,将”urlfetch.fetch”更改为”urlfetch.make_fetch_call”。此时有一点需要注意,不是所有的”urlfetch.fetch”都需要修改的,有些过程是需要顺序执行的,比如同步sina的函数”send_sina_msgs”中的第一个fetch,这个fetch是进行登录并获取Cookie的,改成异步的话此函数需要进行较大的修改,且完全没有必要。具体的修改可参见最后我给出的修改版本。

重复发送的问题

最近月光对twitter-feed进行了修改,以修正TwitterID升为11位后老信息重复获取的问题。但是我更新之后发现新版的twitter-feed仍然会重复发送老数据。月光曾在Twitter里说需要清空一下Datastore。会不会是这个问题呢?事到如今,也只能试一试了。

进入gae的控制台,点击左边的”Datastore Viewer”,在窗体右边将列出存储于数据库中的数据,勾一下列表标题”ID/Name”前面的选择框选中全部记录,点击”Delete”删除。如此这般将所有数据删除后,同步正常了~~~内牛满面哪…… :cry:

后来仔细查看了一下log,似乎是digu的API出了问题,直接通过地址访问会弹出验证对话框,输入用户名和密码之后发现对话框总是不停地弹出来……在加上异步处理和异常处理后,各同步之间保持相对独立,可能这也是我的sina同步恢复正常的原因之一吧~~

附上我的修改版~猛击这里下载. 我只测试了sina和嘀咕,其他的未进行测试,如有问题,欢迎与我联系。 :cool:

阻止直接POST的垃圾评论

上一篇我讲了如何利用隐藏输入框来组织垃圾评论,今天登陆后台,居然还是发现了一篇垃圾评论~ :!: 这是怎么回事?莫非是我的隐藏输入框不起作用了?不应该啊~本地测试都是没问题的。人肉Spam?更加不可能了,哪个foreigner会找到这个没什么人气小站来投放ad啊…

一时半会想不出原因,遂搁置一边,去cPanel转转。在看Last Visitor的时候,忽然发现了如下数据:(因为这件事出现在大约3天之前,Last Visitors的数据已经刷新,下面的截图是Raw Access Log中的数据)imageSpamRobot Access Log对比一下IP,跟spam的一模一样!可以看到,这个Robot先访问到了主页面,之后随机挑选了一篇日志,获得相关数据后直接POST,第三行POST返回代码是302,这是wp-comments-post.php返回的Location,最后它验证了一下,开闪。原来SpamRobot是直接使用POST方法向wp-comments-post.php提交数据的。这帮家伙!

知道了原因,接下来就要想想如何来防止了。如果你是按照上篇的说明修改了自己的主题的话,恭喜你!解决的办法很简单。只要在主题的function.php中加入如下代码就OK啦!

1
2
3
4
5
6
7
function spam_check($comment)
{
	session_start();//开启session
	if(empty($_SESSION['SS_1_ID'])){die("Spam Comment!");}//不存在session值,垃圾评论,结束后续操作
	return $comment;//否则返回评论数据,继续后续步骤
}
add_filter('preprocess_comment', 'spam_check', 1);

如果你使用了Akismet插件,建议修改一下插件代码。以2.2.6版本为例,修改第500行:

add_action('preprocess_comment', 'akismet_auto_check_comment', 1);

把其中的”1″改大一点,比如”5″。这样可以防止产生”潜在”的冲突(我尝试过不修改,似乎也工作正常,但我不能保证)。

主要的思想就是利用session。还记得我在上一篇中的三个随机生成的Session值吗?在处理评论的时候,相关函数会对session进行验证。这里我们可以直接检查是否存在之前生成的随机session值(我这里用的是”SS_1_ID”)。一般的SpamRobot都是直接发送数据,不会保存Cookie。这样在对应于SpamRobot的session中就不存在”SS_1_ID”。

如果你没有使用我的方法改造自己的主题,这里也有个大概的解决办法。前提还是使用session。将commenter的name后拼上某个session值,再到函数中去验证。这个办法我没有尝试,从理论上将应该是没什么问题的。尝试自己解决问题,你会发现很多的乐趣!

当然,如果某个高级的SpamRobot能保存Cookie,这个方法就失效了。怎么办呢?别忘了,咱还有Akismet啊~ ;-)

使用隐藏输入框阻止垃圾评论

看到这个标题,估计你会联想到NoSpamNX。没错,这个idea就是借鉴NoSpamNX的。前几天在网上溜达的时候看到了一篇关于NoSpamNX的介绍,觉得挺不错,决定尝试一下。可惜的是安装之后不能正常使用,估计原因在于我的主题使用了Ajax的评论提交方式,NoSpamNX好像没办法获得评论数据。粗略看了一下代码,觉得也不算太麻烦,遂集成到主题中。

NoSpamNX生成两个Name为随机值的隐藏文本框,其中一个文本框的内容也是随机值,放置在评论表单中,一般的Spam Robot会将此表单所有的项目填充,而正常的访客几乎不可能这么做(当然,无聊地用FireBug看到了这两个文本框然后打算折腾一下的人不算 :!: )。因此,只要判断这两个输入框的内容就知道评论是不是Spam了。

另外,鉴于某些Spam Robot会根据文本框的Name属性来填写内容,我把设计思想改了一下,把真正的留言者名称文本框的Name改成随机值,同时仍然生成两个隐藏文本框,一个Name依然随机,另一个叫”author”,呵呵~~是不是跟YAWASP有点像?好吧,我就是借鉴他的…这样一来,Spam Robot基本上都能被kick out了。

随机值参考NoSpamNX,采用随机数+MD5来产生。生成的数据保存在session里,这样一个访客只用生成一次,直到session过期(默认是24分钟,应该足够了)

啰啰嗦嗦讲了那么多原理,下来开始动手。

增加随机值产生函数

编辑主题的function.php文件,加入如下代码,产生三个随机值。

1
2
3
4
5
6
7
8
9
function Generate_ID()
{
	if (empty($_SESSION['SS_1_ID']))//如果不存在此值,一般是新访问者或者session过期
	{
		$_SESSION['SS_1_ID'] = md5(uniqid(mt_rand(), true));
		$_SESSION['SS_2_ID'] = md5(uniqid(mt_rand(), true));
		$_SESSION['SS_2_VALUE'] = md5(uniqid(mt_rand(), true));
	}//否则不产生新的随机值,使用session中已经保存的
}

修改header.php

修改主题的header.php文件,在适当的位置加入如下代码:session_start();这个适当的位置一般是文件的第3行,即“DOCTYPE”(第一行)和php开始标记(第二行)的下面。

修改评论框(comment.php)

OK,这个算是一个关键部分,请仔细修改并保存备份。

  1. 修改原始的“留言者名称”文本框。找到类似<input type="text" name="author" id="author"的一行,将name换成三个随机值中的一个。我这里改成name="at-<?php echo $SESSION_[SS_ID_1];"
  2. 增加隐藏输入框。找到type=hidden,name=”comment_post_ID”的输入框(<input),在下面一行加入如下代码:
    1
    2
    3
    4
    5
    6
    7
    
    <?php 
    if (mt_rand(1,2) == 1){//两个input位置随机
    	echo '<span id="spamstop" style="display:none"><input type="text" name="author" value="" /><input type="text" name="'.$_SESSION['SS_2_ID'].'" value="'.$_SESSION['SS_2_VALUE'].'" /></span>';
    }else{
    	echo '<span id="spamstop" style="display:none"><input type="text" name="'.$_SESSION['SS_2_ID'].'" value="'.$_SESSION['SS_2_VALUE'].'" /><input type="text" name="author" value="" /></span>';
    }
    ?>

comment.php修改完成~

修改ajax处理文件

打开ajax处理文件(一般是comments-ajax.php),我使用的是这个版本,注意参考。找到$comment_post_ID = (int) $_POST['comment_post_ID'];,在它上面一行开始加入如下代码:

1
2
3
4
5
6
session_start();
if (!empty($_SESSION['SS_1_ID']))
	if (($_POST['author'] != "") ||  //假名称栏有内容
	(!array_key_exists($_SESSION['SS_2_ID'],$_POST)) || //隐藏输入框不存在,可能是直接提交
	($_POST[$_SESSION['SS_2_ID']] != $_SESSION['SS_2_VALUE']) ) //隐藏输入框内容被修改
		fail('Sorry, but your comment seems to be a spam.<br />对不起,您的评论被判定为垃圾评论.');

之后找到"$comment_author = trim(strip_tags($_POST['author']));",将其中的”author”改成在修改评论框中使用的名称,按照我的例子,这里应该改成"$comment_author = trim(strip_tags($_POST['at-'.$_SESSION['SS_1_ID']]));"

到这里,所有修改就完成了。赶快测试一下吧~

Tips

如果主题有设置页面的话,可以给这个功能加一个开关。这样在不想用的时候可以将这个功能关闭。具体的实现就留给大家了,其实是很简单的,参考主题的其他部分应该很容易就能搞定。只是有一点要说一下,如果使用了”开关”来控制这个功能,ajax处理部分也需要获得”开关”的状态,这时不需要在ajax处理文件中再去读取数据库,可以参考comment.php中对”comment-post-id”的传递方式在合适位置加一个hidden类型的input,把”开关”状态放进去,只要保证这个hidden的input在留言表单内,也就是在commentform内,提交的时候就能顺利把参数传过去。

啊~第一次写功能比较复杂的主题修改教程,自我感觉比较烂,还望大家多多包涵,嘿嘿~~

最后要感谢NoSpamNXYAWASP的作者,感谢他想出这么好的点子~~