Nginx CORS实现JS跨域

1. 什么是跨域

简单地理解就是因为JavaScript同源策略的限制,a.com 域名下的js无法操作b.com或是c.a.com域名下的对象。

同源是指相同的协议、域名、端口。特别注意两点:

  • 如果是协议和端口造成的跨域问题“前台”是无能为力的,
  • 在跨域问题上,域仅仅是通过“协议+域名+端口”来识别,两个不同的域名即便指向同一个ip地址,也是跨域的。

2. 跨域解决方案

跨域解决方案有多种,大多是利用JS Hack:

3. CORS

CORS: 跨域资源共享(Cross-Origin Resource Sharing)http://www.w3.org/TR/cors/

当前几乎所有的浏览器(Internet Explorer 8+, Firefox 3.5+, Safari 4+和 Chrome 3+)都可通过名为跨域资源共享(Cross-Origin Resource Sharing)的协议支持ajax跨域调用。(see: http://caniuse.com/#search=cors)

Chrome, Firefox, Opera and Safari 都使用的是 XMLHttpRequest2 对象, IE使用XDomainRequest。XMLHttpRequest2的Request属性:open()、setRequestHeader()、timeout、withCredentials、upload、send()、send()、abort()。

XMLHttpRequest2的Response属性:status、statusText、getResponseHeader()、getAllResponseHeaders()、entity、overrideMimeType()、responseType、response、responseText、responseXML。

启用 CORS 请求

假设您的应用已经在 example.com 上了,而您想要从 www.example2.com 提取数据。一般情况下,如果您尝试进行这种类型的 AJAX 调用,请求将会失败,而浏览器将会出现“源不匹配”的错误。利用 CORS,www.example2.com 服务端只需添加一个HTTP Response头,就可以允许来自 example.com 的请求:

Access-Control-Allow-Origin: http://example.com
Access-Control-Allow-Credentials: true(可选)

可将 Access-Control-Allow-Origin 添加到某网站下或整个域中的单个资源。要允许任何域向您提交请求,请设置如下:

Access-Control-Allow-Origin: *
Access-Control-Allow-Credentials: true(可选)

其实,该网站 (html5rocks.com) 已在其所有网页上均启用了 CORS。启用开发人员工具后,您就会在我们的响应中看到 Access-Control-Allow-Origin 了。

提交跨域请求

如果服务器端已启用了 CORS,那么提交跨域请求就和普通的 XMLHttpRequest 请求没什么区别。例如,现在 example.com 可以向 www.example2.com 提交请求了:

var xhr = new XMLHttpRequest();
// xhr.withCredentials = true; //如果需要Cookie等
xhr.open('GET', 'http://www.example2.com/hello.json');
xhr.onload = function(e) {
  var data = JSON.parse(this.response);
  ...
}
xhr.send();

4. 服务端Nginx配置

要实现CORS跨域,服务端需要这个一个流程:http://www.html5rocks.com/static/images/cors_server_flowchart.png

对于简单请求,如GET,只需要在HTTP Response后添加Access-Control-Allow-Origin。

对于非简单请求,比如POST、PUT、DELETE等,浏览器会分两次应答。第一次preflight(method: OPTIONS),主要验证来源是否合法,并返回允许的Header等。第二次才是真正的HTTP应答。所以服务器必须处理OPTIONS应答。

http://enable-cors.org/server_nginx.html这里是一个nginx启用COSR的参考配置。

流程如下:

  1. 首先查看http头部有无origin字段;
  2. 如果没有,或者不允许,直接当成普通请求处理,结束;
  3. 如果有并且是允许的,那么再看是否是preflight(method=OPTIONS);
  4. 如果是preflight,就返回Allow-Headers、Allow-Methods等,内容为空;
  5. 如果不是preflight,就返回Allow-Origin、Allow-Credentials等,并返回正常内容。

用伪代码表示:

location /pub/(.+) {
    if ($http_origin ~ <允许的域(正则匹配)>) {
        add_header 'Access-Control-Allow-Origin' "$http_origin";
        add_header 'Access-Control-Allow-Credentials' "true";
        if ($request_method = "OPTIONS") {
            add_header 'Access-Control-Max-Age' 86400;
            add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS, DELETE';
            add_header 'Access-Control-Allow-Headers' 'reqid, nid, host, x-real-ip, x-forwarded-ip, event-type, event-id, accept, content-type';
            add_header 'Content-Length' 0;
            add_header 'Content-Type' 'text/plain, charset=utf-8';
            return 204;
        }
    }
    # 正常nginx配置
    ......
}

但是由于Nginx 的 if 是邪恶的,所以配置就相当地让人不爽(是我的配置不够简洁吗?)。下面nginx-spdy-push里/pub接口启用CORS的配置:

# push publish
# broadcast channel name must start with '_'
# (i.e., normal channel must not start with '_')

# GET    /pub/channel_id -> get statistics about a channel
# POST   /pub/channel_id -> publish a message to the channel
# DELETE /pub_admin?id=channel_id -> delete the channel

#rewrite_log on;

# server_name test.gw.com.cn
# listen      2443 ssl spdy

location ~ ^/pub/([-_.A-Za-z0-9]+)$ {

    set $cors "local";

    # configure CORS based on https://gist.github.com/alexjs/4165271
    # (See: http://www.w3.org/TR/2013/CR-cors-20130129/#access-control-allow-origin-response-header )

    if ( $http_origin ~* "https://.+\.gw\.com\.cn(?=:[0-9]+)?" ) {
        set $cors "allow";
    }
    if ($request_method = "OPTIONS") {
        set $cors "${cors}options";
    }

    # if CORS request is not a simple method
    if ($cors = "allowoptions") {
        # Tells the browser this origin may make cross-origin requests
        add_header 'Access-Control-Allow-Origin' "$http_origin";
        # in a preflight response, tells browser the subsequent actual request can include user credentials (e.g., cookies)
        add_header 'Access-Control-Allow-Credentials' "true";

        # === Return special preflight info ===

        # Tell browser to cache this pre-flight info for 1 day
        add_header 'Access-Control-Max-Age' 86400;

        # Tell browser we respond to GET,POST,OPTIONS in normal CORS requests.
        # Not officially needed but still included to help non-conforming browsers.
        # OPTIONS should not be needed here, since the field is used
        # to indicate methods allowed for 'actual request' not the preflight request.
        # GET,POST also should not be needed, since the 'simple methods' GET,POST,HEAD are included by default.
        # We should only need this header for non-simple requests  methods (e.g., DELETE), or custom request methods (e.g., XMODIFY)
        add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS, DELETE';

        # Tell browser we accept these headers in the actual request
        add_header 'Access-Control-Allow-Headers' 'reqid, nid, host, x-real-ip, x-forwarded-ip, event-type, event-id, accept, content-type';

        # === response for OPTIONS method ===

        # no body in this response
        add_header 'Content-Length' 0;
        # (should not be necessary, but included for non-conforming browsers)
        add_header 'Content-Type' 'text/plain, charset=utf-8';
        # indicate successful return with no content
        return 204;
    }

    if ($cors = "allow") {
        rewrite /pub/(.*) /pub_cors/$1 last;
    }

    if ($cors = "local") {
        rewrite /pub/(.*) /pub_int/$1 last;
    }
}

location ~ /pub_cors/(.*) {
    internal;
    # Tells the browser this origin may make cross-origin requests
    add_header 'Access-Control-Allow-Origin' "$http_origin";
    # in a preflight response, tells browser the subsequent actual request can include user credentials (e.g., cookies)
    add_header 'Access-Control-Allow-Credentials' "true";

    push_stream_publisher                   admin; # enable delete channel
    set $push_stream_channel_id             $1;

    push_stream_store_messages              on;  # enable /sub/ch.b3
    push_stream_channel_info_on_publish     on;
}

location ~ /pub_int/(.*) {
  #  internal;
    push_stream_publisher                   admin; # enable delete channel
    set $push_stream_channel_id             $1;

    push_stream_store_messages              on;  # enable /sub/ch.b3
    push_stream_channel_info_on_publish     on;
}

5. 客户端javascript代码

下面是https://spdy.gw.com.cn/sse.html里的代码

var xhr = new XMLHttpRequest();
//xhr.withCredentials = true;
xhr.open("POST", "https://test.gw.com.cn:2443/pub/ch1", true);
// xhr.setRequestHeader("accept", "application/json");

xhr.onload = function()  {
  $('back').innerHTML = xhr.responseText;
  $('ch1').value = + $('ch1').value + 1;
}
xhr.onerror = function() {
  alert('Woops, there was an error making the request.');
};

xhr.send($('ch1').value);

页面的域是https://spdy.gw.com.cn, XMLHttpRequest的域是https://test.gw.com.cn:2443,不同的域,并且Post方式。

用Chrome测试,可以发现有一次OPTIONS应答。如过没有OPTIONS应答,可能是之前已经应答过,被浏览器缓存了'Access-Control-Max-Age' 86400;,清除缓存,再试验。

6. 相关链接

在nginx上使用php代理运行cgi程序

我们用到的很多开源程序比如mailman, nagios等等,都有WEB端管理界面。在那个Apache一家独大的年代,这个问题可以很好解决,因为apache本身可以运行cgi程序。但随着 nginx服务器的大规模应用,而恰好nginx又没有cgi模块,所以我们不得不采用一些变通的手段来解决它。

在网上广为流传的解决方法是一个老外写的perl脚本,但这个脚本本身有很多问题,而且需要在后台启动一个守护进程,本人对用perl写的网络服务守护进程的稳定性很怀疑,在看了它的代码后,发现用PHP即可很好的解决这个问题。

CGI其实本质上就是一个普通的二进制程序,你可以在后台直接运行它。而服务器要做的事就是将WEB传递的变量作为参数传递给这个程序并执行,而将执行返回的结果显示到页面上。

明白了这个道理,我们就可以开始着手解决这个问题了。其过程无非就是将PHP作为一个proxy,使其运行指定的程序,并把程序输出结果echo出来。

我们把这个PHP脚本命名为cgi.php,把它随便放到一个你认为合适的位置,然后用rewrite将后缀为cgi的请求都转发到cgi.php上。以下为参考的配置格式

#rewrite cgi请求到cgi.php上,并把cgi文件名作为php的pathinfo
rewrite ^/nagios/cgi-bin/(.*) /cgi.php/$1 break;
 
location /nagios/
{
    gzip off;
    alias /usr/local/nagios/share/;
    index index.html index.htm index.php;
}
 
location ~ .*\.php(\/.*)*$ {
    fastcgi_pass    127.0.0.1:9000;
    fastcgi_index   index.php;
    include fcgi.conf;
    fastcgi_param SCRIPT_FILENAME /usr/local/nagios/share$fastcgi_script_name;
 
    #pathinfo必须设置
    fastcgi_param  PATH_INFO $fastcgi_script_name;
 
    #以下两个为cgi.php需要用到的变量名,分别为cgi程序目录,和cgi默认index程序
    fastcgi_param  CGI_BASE  /usr/local/nagios/sbin;
    fastcgi_param  CGI_INDEX status.cgi;
}

注意上面配置文件的注释部分,在你自己设置的时候必须填上合适的值。下面就是最重要的cgi.php文件了

<?php
/*
   use php to execute mailman cgi app
   hack by 70 (magike.net@gmail.com)
   https://joyqi.com
 */
 
// get cgi base from fastcgi param
$cgi_base = '';
if (isset($_SERVER['CGI_BASE'])) {
    $cgi_base = rtrim($_SERVER['CGI_BASE'], '/') . '/';
} else {
    die('PLEASE CONFIGURE YOUR CGI_BASE PARAM');
}
 
// get pathinfo
$pathinfo = '';
if (isset($_SERVER['PATH_INFO'])) {
    $pathinfo = $_SERVER['PATH_INFO'];
} else if (isset($_SERVER['CGI_INDEX'])) {
    $pathinfo = $_SERVER['CGI_INDEX'];
} else {
    die('PLEASE CONFIGURE YOUR PATH_INFO PARAM');
}
 
// get real cgi path
$cgi_path = $cgi_base;
$cgi_file = trim($pathinfo, '/');
$cgi_file_levels = explode('/', $cgi_file);
$cgi_file_exists = false;
 
while (count($cgi_file_levels) > 0) {
    $cgi_path = $cgi_path . '/' . array_shift($cgi_file_levels);
 
    if (is_file($cgi_path)) {
        $cgi_file_exists = true;
        break;
    }
}
 
if (!$cgi_file_exists) {
    die('NOT EXISTS PAGE!' . $cgi_file);
}
 
$cgi_pathinfo = '';
if (!empty($cgi_file_levels)) {
    $cgi_pathinfo = '/' . implode('/', $cgi_file_levels);
}
 
if (is_readable($cgi_path)) {
 
    $descriptorspec = array(
            0 => array("pipe", "r"),  // stdin is a pipe that the child will read from
            1 => array("pipe", "w"),  // stdout is a pipe that the child will write to
            2 => array("file", "/tmp/error-output.txt", "a") // stderr is a file to write to
            );
 
    $cwd = $cgi_base;
    $env = $_ENV;
 
    $env['SCRIPT_FILENAME'] = $cgi_path;
    $env['SCRIPT_NAME'] = $cgi_file;
    $env['DOCUMENT_ROOT'] = CGI_BASE;
    $env['PATH_INFO'] = $cgi_pathinfo;
 
    // http auth support (nagios etc.)
    if (isset($_SERVER['PHP_AUTH_USER'])) {
        $env['REMOTE_USER'] = $_SERVER['PHP_AUTH_USER'];
    }
 
    $process = proc_open($cgi_path, $descriptorspec, $pipes, $cwd, $env);
    if (is_resource($process)) {
        $stdin = file_get_contents("php://input");
 
        if (!empty($stdin)) {
            fwrite($pipes[0], $stdin);
            fclose($pipes[0]);
        }
 
        //stream_set_blocking($pipes[1], 0);
        stream_set_timeout($pipes[1], 3);
        $result = stream_get_contents($pipes[1]);
        fclose($pipes[1]);
        $return_value = proc_close($process);
 
        list($header, $body) = preg_split("/\r?\n\r?\n/", $result, 2);
 
        $headers = explode("\n", $header);
        foreach ($headers as $line) {
            header(trim($line));
        }
 
        echo $body;
    } else {
        die('ERROR APPLICATION!');
    }
 
} else {
    die('ERROR PAGE!' . $cgi_path);
}

Nginx配置免费SSL证书StartSSL,解决Firefox不信任问题

先在StartSSL上申请免费一年的SSL证书,具体过程网上很多教程。然后把申请到的key和crt文件上传到服务器,比如/usr/local/nginx/certs/.

 Nginx配置SSL证书

直接贴上我的nginx的部分配置:

server {
        listen 443;
	server_name   liuzhichao.com www.liuzhichao.com ;
        ssl on;
        ssl_certificate /usr/local/nginx/ssl/ssl.crt;
        ssl_certificate_key /usr/local/nginx/ssl/ssl.key;if($http_transfer_encoding ~* chunked){return444;}

	gzip on;if(-d $request_filename){
		rewrite ^/(.*)([^/])$ $scheme://$host/$1$2/ permanent;}

	 root   /home/wwwroot/;

	 ssi off;
	 ssi_silent_errors off;
	 ssi_types text/shtml;

	 location /{
		 index  index.html index.htm index.shtml index.php;
		 autoindex	off;}

	location /nginx_status {
		stub_status on;
		access_log off;}

	 location ~(favicon.ico){ 
		 log_not_found off;
		 access_log   off;}

	 location ~* \.(gif|jpg|jpeg|png|bmp|swf)$ {
		 expires 1y;}

	 location ~* \.(js|css)$ {
		 expires 7d;}#------------
	 location ~*^(.+)\.(php[3-9]?|phtm[l]?)(\/.*)*$ {set $real_script_name $1.$2;set $path_info $3;if(!-f $document_root$real_script_name){return404;}

		  fastcgi_pass 127.0.0.1:8999; fastcgi_param HTTPS on;
		  include enable_php.conf;}}

现在重启Nginx,Chrome应该能正常显示Https.如果只想使用Https连接,可以再添加一个server,然后跳转到https

server {
        listen 80;
	server_name   liuzhichao.com www.liuzhichao.com ;
        rewrite     ^   https://$server_name$request_uri? permanent;}

 解决Firefox不信任StartSSL证书问题

wget http://cert.startssl.com/certs/ca.pem
wget http://cert.startssl.com/certs/sub.class1.server.ca.pem
cat ca.pem sub.class1.server.ca.pem >> ca-certs.crt
cat ca-certs.crt >> ssl.crt

再次重启Nginx,本想这下Firefox也应该能正常识别证书了,但是重启Nginx遇到了SSL: error:0906D066:PEM routines:PEM_read_bio:bad end line error错误。

[emerg]: SSL_CTX_use_certificate_chain_file("/usr/local/nginx/certs/ssl.crt")
 failed (SSL: error:0906D066:PEM routines:PEM_read_bio:bad end line error:140DC009:SSL routines:SSL_CTX_use_certificate_chain_file:PEM lib)
configuration file /usr/local/nginx/conf/nginx.conf test failed

这个的意思就是server.crt读取到意外错误行.这是因为我们在合并StartSSL提供的crt证书时,直接cat到了ssl.crt里。使用vi或者nano命令打开并编辑ssl.crt,找到:

-----END CERTIFICATE----------BEGIN CERTIFICATE-----

修改为:

-----END CERTIFICATE----------BEGIN CERTIFICATE-----

保存这个crt文件,再次重启Nginx服务,输入申请证书时私钥的密码,启动成功后,现在使用Firefox访问网站也能信任证书了。

Discover Meteor Book 中文

Meteor 是一个构建在 Node.js 之上的平台,用来开发实时网页程序。Meteor 位于程序数据库和用户界面之间,保持二者之间的数据同步更新。

因为 Meteor 是基于 Node.js 开发的,所以在客户端和服务器端都使用 JavaScript 作为开发语言。而且,Meteor 程序的代码还能在前后两端共用。

Meteor 这个平台很强大,网页程序开发过程中的很多复杂、容易出错的功能都能抽象出来,实现起来很简单。

那么,你为什么要花时间学习 Meteor,而不去学其他框架呢?拨开 Meteor 的各种功能,我们认为原因只有一个:因为 Meteor 易于学习。

http://zh.discovermeteor.com/pdf

Git工作流指南:Gitflow工作流

常用的分支约定:

用于新建发布分支的分支: develop
用于合并的分支: master
分支命名: release-* 或 release/*

维护分支

git-workflow-release-cycle-4maintenance

维护分支或说是热修复(hotfix)分支用于生成快速给产品发布版本(production releases)打补丁,这是唯一可以直接从master分支fork出来的分支。修复完成,修改应该马上合并回master分支和develop分支(当前的发布分支),master分支应该用新的版本号打好Tag

Bug修复使用专门分支,让团队可以处理掉问题而不用打断其它工作或是等待下一个发布循环。你可以把维护分支想成是一个直接在master分支上处理的临时发布。

 

https://github.com/oldratlee/translations/blob/master/git-workflows-and-tutorials/workflow-feature-branch.md

 

 

 

自定义元素:在 HTML 中定义新元素

Eric Bidelman
翻译: 米粽 (Leo Deng) 已发布: 八月 28th, 2013  已更新: 十二月 18th, 2013

This article discusses APIs that are not yet fully standardized and still in flux. Be cautious when using experimental APIs in your own projects.

引言

现在的 web 严重缺乏表达能力。你只要瞧一眼“现代”的 web 应用,比如 GMail,就会明白我的意思:

现代 web 应用:使用

堆砌而成。
堆砌

一点都不现代。然而可悲的是,这就是我们构建 web 应用的方式。在现有基础上我们不应该有更高的追求吗?

时髦的标记,行动起来!

HTML 为我们提供了一个完美的文档组织工具,然而 HTML 规范定义的元素却很有限。

假如 GMail 的标记不那么糟糕,而是像下面这样漂亮,那会怎样?

真是令人耳目一新!这个应用太合理了,既有意义,又容易理解。最妙的是,它是可维护的,只要查看声明结构就可以清楚地知道它的作用。

自定义元素,救救我们!就指望你了!
赶紧开始吧

自定义元素 允许开发者定义新的 HTML 元素类型。该规范只是 Web 组件模块提供的众多新 API 中的一个,但它也很可能是最重要的一个。没有自定义元素带来的以下特性,Web 组件都不会存在:

定义新的 HTML/DOM 元素
基于其他元素创建扩展元素
给一个标签绑定一组自定义功能
扩展已有 DOM 元素的 API
注册新元素

使用 document.registerElement() 可以创建一个自定义元素:

var XFoo = document.registerElement(‘x-foo’);
document.body.appendChild(new XFoo());
document.registerElement()

的第一个参数是元素的标签名。这个标签名必须包括一个连字符(-)。因此,诸如 、 和 都是合法的标签名,而 和 <foo_bar> 则不是。这个限定使解析器能很容易地区分自定义元素和 HTML 规范定义的元素,同时确保了 HTML 增加新标签时的向前兼容。

第二个参数是一个(可选的)对象,用于描述该元素的 prototype。在这里可以为元素添加自定义功能(例如:公开属性和方法)。稍后详述。

自定义元素默认继承自 HTMLElement,因此上一个示例等同于:

var XFoo = document.registerElement(‘x-foo’, {
prototype: Object.create(HTMLElement.prototype)
});

调用 document.registerElement(‘x-foo’) 向浏览器注册了这个新元素,并返回一个可以用来创建 元素实例的构造器。如果你不想使用构造器,也可以使用其他实例化元素的技术。

如果你不希望在 window 全局对象中创建元素构造器,还可以把它放进命名空间(var myapp = {}; myapp.XFoo = document.registerElement(‘x-foo’);)。

然后直接上代码了~~

<!DOCTYPE html>
<!–
Copyright 2013 The Polymer Authors. All rights reserved.
Use of this source code is governed by a BSD-style
license that can be found in the LICENSE file.
–>
<html>
<head>
<title></title>
<meta charset=”UTF-8″>
<link rel=”import” href=”test-component.html”>
</head>
<body>

<x-foo-shadowdom></x-foo-shadowdom>
<script>

var XFooProto = Object.create(HTMLElement.prototype);

XFooProto.createdCallback = function() {
// 1. 为元素附加一个 shadow root。
var shadow = this.createShadowRoot();

// 2. 填入标记。
shadow.innerHTML = “<b>I’m in the element’s Shadow DOM!</b>”;
};

var XFoo = document.registerElement(‘x-foo-shadowdom’, {prototype: XFooProto});

</script>
</body>
</html>

http://www.html5rocks.com/zh/tutorials/webcomponents/customelements/#intro

Mac 电脑 装机必备软件 推荐

一、输入法

Mac 自带的中文输入法确实很不好用,因此建议安装第三方输入法,至于搜狗、QQ、百度三个输入法的选择,主要看个人习惯了,功能上差不多,笔者使用的是搜狗。

1.搜狗输入法:http://ifunapple.com/2014/06/sougou-pinyin-29/
2.QQ输入法:http://ifunapple.com/2013/09/qqime/
3.百度输入法:http://ifunapple.com/2013/06/baidu-ime/

二、浏览器

Mac 上的流行浏览器和 Windows 上差不多,除了 IE 被 Safari 替代,个人感觉 Chrome 性能最高,Safari 也不错,而 Firefox 作为开发者一定要安装。
1.Chrome:http://ifunapple.com/2014/08/chrome-36/
2.Safari:系统自带
3.Firefox:http://ifunapple.com/2014/08/firefox-31/

三、下载工具

Mac 上的下载工具并不多,大部分时候使用浏览器直接下载就可以,迅雷和Folx是比较不错的两款下载工具。
1.迅雷:http://ifunapple.com/2014/06/thunder-2-6/
2.Folx:http://ifunapple.com/2014/05/folx-go/

四、看图工具

Mac 系统默认使用「预览」打开图片,但「预览」最大的不足是不支持图片上一张、下一张的切换,很不方便,因此建议安装第三方看图软件
1.ArcSoft Photo+:http://ifunapple.com/2013/12/arcsoft-photo-3/

五、音乐播放器

音乐播放直接使用iTunes就可以,而在线音乐播放软件目前「百度音乐」和「QQ 音乐」做的比较不错,其中百度音乐支持音乐下载。
1.百度音乐:http://ifunapple.com/2014/01/baidu-music-103/
2.QQ 音乐:http://ifunapple.com/2014/08/qq-music-1-4/
3.iTunes:系统自带

六、视频播放器

Quicktime 支持的视频格式太少了,MPlayerX和KPlayer都可以作为首选视频播放器,支持几乎所有常见的视频格式。
1.MPlayerX:http://ifunapple.com/2014/03/mplayerx-1022/
2.KPlayer:http://ifunapple.com/2014/05/kplayer-143/

七、压缩解压工具

BetterZip和Entropy都是很不错的压缩解压缩工具,支持 ZIP、RAR、TAR 等几乎所有常见的压缩格式。
1.BetterZip:http://ifunapple.com/2014/03/betterzip-233/
2.Entropy:http://ifunapple.com/2013/12/entropy/

八、聊天社交工具

1.QQ:http://ifunapple.com/2014/05/qq-3-1-2/
2.微信:http://ifunapple.com/2014/02/weichat/

九、系统增强

这六款软件是站长认为必须安装的系统增强工具,能够大大提升使用 Mac 的效率
1.Parallels Desktop 9:Mac 上最好用的虚拟机,虽然说 Mac 上的软件已经够多,但肯定有一些软件需要你必须在 Windows 上运行,比如网银插件等,因此安装个 Windows 系统以备不时之需,但站长不建议安装双系统,主要是来回切换太麻烦了,建议在 Mac 上安装Parallels Desktop虚拟机,然后在虚拟机中安装 Windows。

http://ifunapple.com/2014/07/parallels-desktop-9-0-24237/
2.Paragon NTFS:解决你的移动硬盘或 U 盘在 Mac 上只能读不能写的问题。
http://ifunapple.com/2014/04/ntfs-11-2/

3.CleanMyMac:系统清理和应用卸载工具。
http://ifunapple.com/2014/07/cleanmymac-2-2-7-b1/

4.iStat Menus:系统监控工具,如 CPU、网速、温度等等。
http://ifunapple.com/2014/08/istat-menus-5/

5.TotalFinder:Finder 增强工具,如剪切、多 Tab、显示隐藏文件、复制路径等。
http://ifunapple.com/2014/07/totalfinder-1-6-2/

6.BetterSnapTool:窗口大小和位置控制工具,比如拖拽窗口完成最大化。
http://ifunapple.com/2014/05/bettersnaptool/

十、文档办公

Mac 上的办公软件就是微软的Office和苹果的iWork了,如果习惯了 Office,就安装 Office 吧。
1.Microsoft Office:http://ifunapple.com/2014/06/office/
2.iWork:http://ifunapple.com/2013/10/iwork-2013/

十一、其他

1.邮件客户端:推荐使用系统自带的
2.PDF阅读工具:推荐使用系统预览应用
3.文本编辑:简单的使用系统的「文本编辑」就够用了

http://ifunapple.com/2014/08/subject-zjbb/

初创团队如何提高工作效率?

http://jianshu.io/p/e61e458dbfe0

条件
初创团队:3-5人(或远程)。
工作效率:
问题1:和大公司不同,没有成熟的架构、流程,一个人往往要承担多人的工作。
问题2:时间窗问题,如果效率不高,短时间内做不到一定的质和量,就要被竞争对手搞死啦~
所以:工作效率对于早期初创团队至关重要。
本质:专注核心业务。以最短的资金和时间投入,完成非核心、但是必须要完成的事情。
要素
要素1:不要重新发明轮子。
要素2:以最低的成本(包含资金和学习成本),发现和使用最高效的工具。
个人时间管理
日历
Google:国内速度极慢,不间断被屏蔽。
Calendar(Apple):苹果设备间同步。
重点:个人时间管理。我没有用Any.do、WunderList等。暂时用不同颜色,来区分:1、公司项目,2、兴趣项目,3、个人安排。技巧:每周末需要花足够多的时间,安排下周的工作,确定重点。
硬件配置
多屏幕切换:扩展屏幕。浏览器、Evernote等放在大屏幕。聊天窗、邮件回复等放在小屏幕。
键盘、鼠标:对于每天和电脑打交道的人来说,一个机械键盘真的能提高打击的愉悦感和效率。
Mac/iPad/iPhone:Mac处理工作。iPad回复邮件。iPhone回复微信。
重点:很多人以为是装逼,其实真的是,也不全是。多屏幕解决的第1个问题是:同步处理多件小事情。举例:日历、微信、QQ、换歌同步处理。多屏幕解决的第2个问题是:平行的思考一件大事情的几个部分。举例:浏览器查资料、Evernote记录内容、回邮件。
思考内容沉淀
简书:把重要的内容沉淀下来,避免重新思考一样的事情,方便把问题的核心复制、传递~
翻墙
云梯:不解释…云梯的一个好处是,可以自由选择台湾、日本、美国、香港等不同线路,选择其中速度最快的一条。
公司内部管理
项目管理
Fengche:前一个月免费,后续收费。
TeamBition:对于互联网、设计、广告、传媒行业有流程优化。
Pivotal Tracker:程序员专用利器。
重点:任务的布置最好由Team Leader来做。这是一个思考项目整体的机会,以及静下来思考每个任务如何落地操作、难以程度、安排多少时间、几个人、任务顺序、优先级、性价比的最好的机会。即是Team Leader静下心来反思整体项目最好的机会。
文件共享工具
百度云盘:酷盘自从被收购之后,整个人都不好了…
Dropbox:对于小团队来说Dropbox基本够用了,功能也够全(或者Box)。
Google Drive:需翻墙,你懂得…
重点:合理地利用文件共享工具,能够避免很多邮件传来传去的时间。另外,高阶的文件协同编辑的功能也可以考虑~
实施会议
Fuze:远程团队沟通非常适用。
iPhone:如果只是通话,苹果自带的多人通话已经非常好用。
微信群
微信:多人工作交流不二之选。
重点:合理设置群名称、针对性挑选置顶群是一门学问。
Evernote
项目知识树链条:TeamBition、Evernote、简书。Evernote在整条知识树链条中处于中间环节。上游由TeamBition来梳理项目的整体和每个任务。下游由简书来沉淀思考、进行分享。Evernote在中游承担零星知识点的积累、梳理、归档的功能。Evernote同时也可以部分承担CRM管理、项目管理的职责。
重点:技巧1:合理地设置笔记本组、笔记本、笔记的三层关系。技巧2:充分适用Evernote提供的小功能,比如:演示、分享等。
企业邮箱
QQ企业邮箱:免费、设置方便、速度快、超大附件上传。
客户关系管理
MikeCRM:HighRise在国内的速度实在太慢。
重点:MikeCRM的几项功能:XLS导入联系人、联系人分组、邮件群发、在线问卷调查/回收。
其它
财务管理:挖财。
易耗品管理(InnoSpace广告贴):R2
项目运营
活动发布平台(做活动)
活动行:针对国内。
EventBrite:针对国外。
视频制作(做视频)
Powtoon:傻瓜式视频制作工具,整个视频制作过程和PPT制作类似。长于1分钟的视频,收费15美金/月。
重点:对于质量要求比高的视频,字幕和背景音乐是个难点。
海报(做海报)
简图:iOS APP,简洁的排版、文字少、速度快。微信转发适用~
Phoster:iOS APP,模板多。
海报工场:iOS APP,模板多(美图秀秀出品)。
Canvas:适合对设计要求较高的人群。
核心:学会”Say No”
提高工作效率的目的是:争取更多的时间段,在状态最好的时间窗内,集中处理最重要的事情~
最核心的技巧是:学会”Say No”。不要让不重要的人和事打断你。如果不确定,要有能力在最短的时间内做出判断,然后坚定地”Say No”。
“一个好销售的核心能力,是能在最短的时间内精确判断这个人是不是值得花时间去争取的客户。”

初创团队工作流与开发模式探讨

2012年版本

http://saturn.yoozi.org/iTalk/#title-slide

初创团队工作流与开发模式探讨胡杨刚(a.k.a. Saturn)
Slides and Source on Github Me 2012/11/18 ku6.com, Beijing Video
主题 Walkthrough

初创团队协作工具选型及各自角色
高效协作开发三剑客:Basecamp、Github、QQ 企业邮箱

基于 Git-Flow 的 Web 开发模型
简单介绍 Git 及 Git-Flow 开发模型

使用 Git 对 CI 应用进行自动化部署
Case Study: 利用 Github Service WebHook 调用脚本执行自动化部署

协作/开发工具选型标准 Considerations

体验:是否简单易用?
降低因引入新的工作软件/模式所带来的学习成本。

协作:是否方便多人远程协作?
完全线上协作,集中资源闹革命;不必再为无法找到合适的本地人才而烦恼。

信息整合:是否支持丰富的 API 功能?
API 方便将团队所有工具信息流进行整合、归档,形成团队专属知识库/Bug集中营。

成本:是否需要花费精力维护?是否便宜?
选用在业界具有知名度的 SaaS 型工具,降低软件维护成本和财务成本。

推荐方案 Recommendation

适合 10~50 人之间的开发团队,每月固定财务花费约 200 USD(~ 1400 CNY)。

Basecamp:产品设计与规划执行
用例:产品功能讨论(Messages)、文档协同撰写(Writeboards)、待办事项(To-Dos)、团队日历(Calendar)和时间管理(Time Tracking)

Github:源代码管理与缺陷跟踪
用例:代码托管(Git)、产品功能路线图(Milestone)、代码审查(Code Review)、Bug 跟踪(Issues)

QQ 企业邮箱:信息中心与消息推送
用例:通过绑定 QQ 或微信,将开发相关任务即时、准确的传达到每位订阅成员。

工作信息流 A Workflow for startup

Basecamp:制定产品规划与开发分工
QQ企业邮箱:成员参与讨论与制定规划
微信/QQ:推送订阅信息到指定成员
Github:开发所需功能、缺陷跟踪
生产环境服务器:自动部署上线

Why Git?

Git – the stupid content tracker
Git Manual
相较集中式版本控制工具 CVS / SVN 等,Git 的特点主要包括:

分支(Branching)成本较低。
合并(Merging)操作简单直观。
分布式离线操作。
Why Git?

Photo credit to nvie.com.

Git-Flow:一个 Git 分支模型

Photo credit to nvie.com.

简单部署模型:架构

Photo credit to https://github.com/logsol/Github-Auto-Deploy.

开发人员将代码 push 到 Github 远程服务器(Remotes/Origin)时:

触发 Git Post-Receive Hook(Git Origin 接收并处理完当前 Push 请求后均执行此钩子)。
Github 将当前 push 信息以 HTTP POST 方式调用给事先定义好的 WebHook Url(s)。
WebHook Url(s) 对应一个或多个专门用于处理产品部署逻辑的 HTTP 服务。
HTTP 服务接收 POST 数据、执行部署逻辑(如压缩合并文件和重置缓存等)、完成部署。
简单部署模型:HTTP 服务配置

HTTP 部署服务需以 Daemon 形式长期驻留系统,推荐使用 Python/Node.js 编写。

{
“port”: 8001,
“repositories”:
[{
“url”: “https://github.com/cnsaturn/iTalk”,
“path”: “/your/local/path/on/target/server/here”,
“deploy”: “make deploy” // 自定义 makefile
}]
}
简单部署模型:HTTP 服务

采用 Python 编写、用于解析 Post-Receive Hook POST 请求的 HTTP 示例服务核心逻辑:

def pull(self, path):
if(not self.quiet):
print “\nPost push request received”
print ‘Updating ‘ + path
call([‘cd “‘ + path + ‘” && git pull’], shell=True)

def deploy(self, path):
config = self.getConfig()
for repository in config[‘repositories’]:
if(repository[‘path’] == path):
if ‘deploy’ in repository:
if(not self.quiet):
print ‘Executing deploy command’
call([‘cd “‘ + path + ‘” && ‘ + repository[‘deploy’]], shell=True)
break
简单部署模型:部署逻辑(1)

程序部署除代码直接更新外,通常我们还需要处理如下问题:

前端代码优化
如压缩合并 JavaScript/CSS 文件、压缩和优化图片、CDN 文件同步。

更新程序运行环境变量
如 CI 入口文件 index.php 中的 ENVIRONMENT 常量。

数据库结构变更
重置/预加载系统缓存等其他部署逻辑。
简单部署模型:部署逻辑(2)

为了解决此问题,通常会使用 Make / Ant / Maven 等 Build 工具来实现自动化操作:

前端文件的处理使用比如 uglifyjs(npm包)、jshint(npm包)、yuicompressor(jar包)来实现。
环境变量的替换可以事先制作成模板,然后按需替换。
数据库结构变更在 CI 中可使用 DB Migration 库轻松实现。