ngrok内网穿透

ngrok 内网穿透利器

  由于开发Web项目,经常需要将本地部署的网站让外网能直接访问到,最便捷的做法当然是在ADSL路由器上直接做端口映射,很不幸大部分运营商都屏蔽了80等常用端口,曾经的做法是在公网一台VPS上架设OpenVPN,将笔记本和VPS连到一个虚拟局域网,再用iptables做端口转发来达到目的,虽然可行,但速度比较慢,由于线路不稳定造成掉线几率较高。偶然发现还有个叫ngrok的神器专门做了这件事,不但提供了一个在外网能够安全的访问内网Web主机,还能捕获所有请求的http内容,方便调试,甚至还支持tcp层端口映射,不局限于某一特定的服务。支持Mac OS X,Linux,Windows平台。

. ngrok下载运行

  体积很小,官网下载后直接解压得到一个二进制文件,在shell中执行./ngrok 80即可,默认会分配随机的二级域名来访问,转发到本机的80端口。可以通过-help参数来查看详细的说明,运行后如下提示:

    Tunnel Status                 online
    Version                       1.6/1.5
    Forwarding                    http://steven-mbp.ngrok.com -> 127.0.0.1:8080
    Forwarding                    https://steven-mbp.ngrok.com -> 127.0.0.1:8080
    Web Interface                 127.0.0.1:4040
    # Conn                        16
    Avg Conn Time                 558ms

  我这里是使用了自定义二级域名,意味着访问http://steven-mbp.ngrok.com就如同访问内网的http://127.0.0.1:8080,很方便吧。通过ngrok提供的管理界面(127.0.0.1:4040)可以清楚的看到当前有哪些连接,以及请求的url,可以进行replay。

2. ngrok常用示例

  1. 采用自定义二级域名steven-mbp.ngrok.com转发到本机的8080端口。
    ./ngrok -subdomain steven-mbp 8080
  1. tcp端口转发,这意味着可以在外网ssh到本机了,当然外网端口是随机分配的。
    ./ngrok -proto=tcp 22
  1. 转发到局域网其他的机器
    ./ngrok 192.168.0.1:80
  1. 绑定顶级域名(付费才可用),在dashboard中添加域名,将域名cname解析到ngrok.com即可。
    ./ngrok -hostname test.dorole.com 8080

3. ngrok配置文件

  ngrok可以将参数写到文件中,默认是放在~/.ngrok。例如:

    tunnels:
    client:
        auth: "user:password"
        proto:
        https: 8080
    ssh:
        proto: 
        tcp: 22
    test.dorole.com
        proto:
        http: 9090

  这里定义了三个隧道,client表示转发http到本机8080,同时要求验证,ssh表示支持远程访问,第三个是绑定了域名转发到9090。这时候只需要一个./ngrok start client ssh test.dorole.com即可快速启动这三个隧道服务。

  每一个隧道的配置节点都有五个参数,proto,subdomain,auth,hostname和remote_port,每个隧道必须有proto参数来指定本地地址和端口。auth参数用于在http(s)中身份认证,而remote_port用于在tcp隧道中指定远程服务器端口。如果没有配置subdomain参数,ngrok会默认一个二级域名与隧道节点一样的名字。

4. 配置文件中的其他参数

    authtoken: abc123
    inspect_addr: "0.0.0.0:8888"
    tunnels:
    ...

  authtoken用于设置登录ngrok的授权码,可以在ngrok首页的dashboard中查看到。inspect_addr用于设置监听ip,比如设置为0.0.0.0:8080意味着监听本机所有ip的8080端口上。ngrok也支持自己架设ngrokd服务器,在配置中通过server_addr: “dorole.com:8081″来指定自己搭建的服务器地址。设置trust_host_root_certs: true来支持TLS加密协议的证书信任。ngrok支持http proxy,可以将ngrok配置成一个http代理,这在本机网络受限制的地方用比较合适。

域名

3w429299t4.zicp.vip
http://34ji293130.wicp.vip/github-webhook/

angularjs绑定html标签

方法一使用ngSanitize

angular.module(‘app’).filter(‘unsafe’, function($sce) { return $sce.trustAsHtml; });
在angular 1.2 之前是直接通过 ng-bind-html-unsafe=”contenxt”,之后angular 官方去掉了这样直接的绑定方法
直接通过$sce的$sce.trustAsHtml 取代,
但是我们也可以通过使用指令的方式达到相同的目的

方法二自定指令

不用ngSanitize

module.directive(‘html’, function() {
function link(scope, element, attrs) {

    var update = function() {
        element.html(scope.html);
    }

    attrs.$observe('html', function(value) {
        update();
    });
}

return {
    link: link,
    scope:  {
        html:   '='
    }
};

});
How to use:

angularjs-ui路由权限篇

事件捕获

用户是否可以进入哪个页面,最好在页面加载还未渲染跳转页面时进行判断,
第一种解决办法:在全局简历MainController

1
2
3
4
5
6
7
8
$rootScope.$on('$stateChangeStart',function(event, toState, toParams, fromState, fromParams){
if(toState.name=='login')return;// 如果是进入登录界面则允许
// 如果用户不存在
if(!$rootScope.user || !$rootScope.user.token){
event.preventDefault();// 取消默认跳转行为
$state.go("login",{from:fromState.name,w:'notLogin'});//跳转到登录界面
}
});

路由跳转配置

在config中配置$stateProvider.state()中的reslove中如果接收的是一个promise对象若抛出的是deferred.reject()页面是不跳转的.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
$stateProvider.state('default',{
url:'',
resolve:{
guarder:function($q,$http){
var allowed = false;
var deferred = $q.defer();
if(allowed){
deferred.resolve();
} else {
deferred.reject();
}

return deferred.promise;
}
},
templateUrl:'controllers/home/index.html',
controller:'HomeIndexController as vm'
});

Controller中$location判断

在controller中手动判断用户登陆状态,一般将用户数据存储在rootscope中,保持html模板和controller都能调用到且相互独立,当然也能放到自己定义的service中.

总结:

三种方式各有优缺点,需要更具具体需求,判断要用哪个.

angular服务篇

运用范围

Controller: 只用来处理简单的页面逻辑, 及处理View层,为View-Model修饰层,数据在Controller里被绑定道View-Model层即$scope,最后通过$digest循环来刷新页面.
Service: 用来处理复杂的数据逻辑,与后台服务器通信构成Model层,与Controller互联完成页面的数据传输.

Service

  1. Factory
  2. Service
  3. Provider

Factory

创建一个数据结构,包括对象和函数,并将其返回,在Factory 加载时被调用,后台只保留一份.但是当返回函数时可以通过new操作符,创建独立作用域数据.

Service:

返回一个this 对象当然也可以不返回,默认为this.对象本身会在controller 调用时被创建一次,但仅仅只会被创建一次,属于单利模式.

Provider

在angular载入时加载ProviderProvider,是唯一一种在angular运行开始时传入.config的服务,如果要进行模块范围的配置就用provider.在被Controller调用时,只会调用this.$get函数中的返回值

实现

Provider –> Factory –> Service

斐波那契数列的求法

斐波那契数列如下:

0,1,1,2,3,5,8,13,21,34,55,...

该序列为数列的当前项为此当前项的前两项之和。即:

recurFib(n) = recurFib(n-1)+recurfib(n-2);

递归方式

递归方式思考比较简单由上面的公式直接得出,难点在于边界值的判断。但是这个算法本身的计算会产生一个二叉数组,数组的节点在某些值上会重复,所以可以对这个算法进行改进。以避免重复运算

function recurFib(n) {
    if(n<2){
        return n;
    } else {
        return recurFib(n-1) + recurFib(n-2);
    }

}


console.log(recurFib(10));//55

动态规划版

function iterFib(n) {
    var last = 1;
    var nextLast = 1;
    var result = 1;

    if(n==0) {
        return 0;
    }
    
    for(var i =2 ; i < n ;i ++) {
        result = last+nextLast;
        nextLast = last;
        last = result;
    }

    return result;
}

总结:

就比较而言动态规划法去掉了一些重复步骤,加快了运行速度

ES6设计模式

我认为设计模式虽然不长用,特别是在js这种函数式编程中,但是如果我们掌握了这些东西,会使我们的代码结构更优美,看起来更流畅,不一定比其他方式更好,但是会更符合个人的开发风格,我的开发风格就是_懒_.

单例模式

单例模式主要是在类new之后调用同一份数据,有点想其他语言的静态类,主意的是,ES6中所有的类都必须new了之后才能使用。所以单例模式就比较重要。

    "use strict";

    let __instance = function(){
        let instance ;
        return (newInstance) => {
            if(newInstance) instance = newInstance;
            return instance;
        };                                                                                                   
    }();

    class Singleton {
      constructor() {
        if(__instance()) return __instance();
        __instance(this);
      }

    }

    class test extends Singleton {
        constructor(){
            super();
            this.foo = 'bar';
        }
    }


    let u1 = new test();
    let u2 = new test();

    console.dir(u1); //'bar'
    console.log(u1 === u2); //true

订阅/发布模式(事件)

这个没什么好说的,我在自己写了之后才发现原生的node本身有自己的事件,一下是代码,采用nodejs 的继承机制我觉得会在一个的过程中特别方便,唯一觉得比较坑的就是在事件的删除上,自己写的时候想的太复杂,后来想了一下,有些时候排序能解决很多问题;就比如这句:

    indexs.sort().reverse().forEach((item, index, array) => {
        subs.get(type).splice(item - 1, 1);
    });

在删除中,如果排序那么,在删除的过程中就会产生数组的长度的变化,从而影响到传入数组indexs无法匹配原有数组的情况,所以这里采用先排序后反转,从大到小,依次处理就不会影响indexs 中值得索引变化。

nodejs

    const EventEmitter = require('events');

    class MyEmitter extends EventEmitter {}

    const myEmitter = new MyEmitter();
    myEmitter.on('event', () => {
      console.log('an event occurred!');
    });
    myEmitter.emit('event');

macl

    'use strict';
    class Event {
        constructor() {
            this.subscribers = new Map([
                ['any', []]
            ]);
        }

        on(type = 'any', fn) {
            let subs = this.subscribers;
            if (!subs.get(type)) return subs.set(type, [fn]);
            subs.get(type).push(fn);
            // subs.set(type, );
        }

        emit(type = 'any', content) {
            if (!this.subscribers.get(type)) return;
            for (let fn of this.subscribers.get(type)) {
                fn(content);
            }
        }

        remove(type = 'any', ...indexs) {
            let subs = this.subscribers;
            if (indexs.length === 0) return subs.delete(type)
            indexs.sort().reverse().forEach((item, index, array) => {
                subs.get(type).splice(item - 1, 1);
            });
        }


        get(type = 'any') {
            let subs = this.subscribers;
            return subs.get(type);
        }
    }

    export default Event;

    // class Test extends Event {
    //     constructor() {
    //         super();
    //     }
    // }

    // let event = new Test();


    // event.on('myEvent', (content) => console.log(`1get published content: ${content}`));
    // event.on('myEvent', (content) => console.log(`2get published content: ${content}`));
    // event.on('myEvent', (content) => console.log(`3get published content: ${content}`));
    // event.on('myEvent', (content) => console.log(`4get published content: ${content}`));
    // event.on('myEvent', (content) => console.log(`5get published content: ${content}`));

    // event.remove('myEvent', 1, 4, 3);

    // event.emit('myEvent', 'jaja'); //get published content: jaja

总结

设计模式只是为了编程的更加优美,并不是说所有的都能被固化,从而在所有的地方都被用到,而要结合具体的环境而编写适合的模式,这里列出的都只是目前我能将之抽象出来的,还有很多以后会补上。

EJS

之前一直没有用过EJS,一直用的jade,但是越用jade,越觉得好像被框在了圈圈里,倒不是jade的语法问题,相反我觉得jade从语法角度而言是最好的一门模版语言,对比ejs没有额外的添加代码,甚至是比html更好,但是jade由于他的简洁也造成了一个问题,如果没有html2jade 的转义很难和html相结合,特别是大多数人使用的前端模版,都是建立在html上的,所以如果不是项目开始就用jade的话就特别别扭,但是EJS却没有这类的问题,当然也付出了相应的代价在代码中看到让人眼睛发晕的<% %>标签,特别让人不舒服,这也是在刚开始学习使用模版的时候没有选择EJS的原因

example

jade

    !!!
    html
        head
            title #{title}
            meta(charset="UTF-8")
        body
            div.description #{description}
            ul
                - each data in datas
                    li.item(id='item_'+data.index)
                        span= data.time
                        a.art(href=data.url)= data.title

ejs

    <!doctype html>
    <html>
    <head>
        <meta charset="UTF-8">
        <title><%=title%> - Page Test</title>
    </head>
    <body>
        <div class="description"><%=description%></div>
        <ul>
    <% function data(data) { %>
            <li class="item" id="item_<%=data.index%>"><span><%=data.time%></span><a href="<%=data.url%>" class="art"><%=data.title%></a></li>
    <% } %>
    <% datas.map(data) %>
        </ul>
    </body>
    </html>

语法

标签

  1. <% ... %> 用来包含要执行的JS代码,凡是不需要输出文本的地方都用它

  2. <%= ... %> 用于输出文本,被包含的值解析后转换成文本

  3. <%- ... %> EJS内部语法,不进行转义,如内部模版嵌套

  4. <%% ... %> 输出<%

  5. <%# 内部注释

  6. -%> 换行符
    如果不喜欢<% ...%> 也可以换成其他的

     var ejs = require('ejs');
     ejs.open = '{{';
            ejs.close = '}}';
    

语法

  1. EJS 直接使用js内嵌,用编程的方式写html,如:

     <% if (user) { %>
         <h2><%= user.name %></h2>
     <% } %>
    
  2. 其主要有两种用法,一个是直接返回一个Function函数,参数为填充的数值对象,另一种是直接返回String类型的html,如:

     var template = ejs.compile(str, options);
     template(data);
     // => Rendered HTML string
    
     ejs.render(str, data, options);
     // => Rendered HTML string
    
  3. 其中options的一些参数为:

    1. cache:是否缓存解析后的模版,需要filename作为key;
    2. filename:模版文件名;
    3. scope:complile后的Function执行所在的上下文环境;
    4. debug:标识是否是debeg状态,debug为true则会输出生成的Function内容;
    5. compileDebug:标识是否是编译debug,为true则会生成解析过程中的跟踪信息,用于调试;
    6. client,标识是否用于浏览器客户端运行,为true则返回解析后的可以单独运行的Function函数;
    7. open,代码开头标记,默认为’<%’;
    8. close,代码结束标记,默认为’%>’;
    9. 其他的一些用于解析模版时提供的变量。
      在express中使用时,options参数将由response.render进行传入,其中包含了一些express中的设置,以及用户提供的变量值。
  4. 此外ejs还提供了一些辅助函数,用于代替使用javascript代码,使得更加方便的操纵数据。

    1. first,返回数组的第一个元素;
    2. last,返回数组的最后一个元素;
    3. capitalize,返回首字母大写的字符串;
    4. downcase,返回字符串的小写;
    5. upcase,返回字符串的大写;
    6. sort,排序(Object.create(obj).sort()?);
    7. sort_by:’prop’,按照指定的prop属性进行升序排序;
    8. size,返回长度,即length属性,不一定非是数组才行;
    9. plus:n,加上n,将转化为Number进行运算;
    10. minus:n,减去n,将转化为Number进行运算;
    11. times:n,乘以n,将转化为Number进行运算;
    12. divided_by:n,除以n,将转化为Number进行运算;
    13. join:’val’,将数组用’val’最为分隔符,进行合并成一个字符串;
    14. truncate:n,截取前n个字符,超过长度时,将返回一个副本
    15. truncate_words:n,取得字符串中的前n个word,word以空格进行分割;
    16. replace:pattern,substitution,字符串替换,substitution不提供将删除匹配的子串;
    17. prepend:val,如果操作数为数组,则进行合并;为字符串则添加val在前面;
    18. append:val,如果操作数为数组,则进行合并;为字符串则添加val在后面;
    19. map:’prop’,返回对象数组中属性为prop的值组成的数组;
    20. reverse,翻转数组或字符串;
    21. get:’prop’,取得属性为’prop’的值;
    22. json,转化为json格式字符串
  5. EJS 模版嵌套<%- include ... %>

     <ul>
       <% users.forEach(function(user){ %>
         <% include user/show %>
       <% }) %>
     </ul>
    

总结:

EJS和jade之间其实没必要太过关注,他们都是模版语言,只要你喜欢只要合适,更具具体的项目选择合适的模版就行。