Enabling SSL with Let’s Encrypt, NGINX and Docker

Recently, I read a lot of articles on how to enable ssl with certbot, nginx and docker, but all the methods I read did not work for me untill I found this methods listed below. Thanks to all the authories who wrote the articles in the references.

Steps

  • Setting up Nginx and Certbot on your host server
  • Obtaining an SSL Certificate on your hose server
  • Map your host letsencrypt folder to nginx in docker

Detail

Step 1 Setting up Nginx and Certbot on your host server

$ sudo apt-get update
$ sudo apt-get install software-properties-common
$ sudo add-apt-repository ppa:certbot/certbot
$ sudo apt-get update
$ sudo apt-get install python-certbot-nginx certbot nginx

Setting up Nginx

$ sudo vim /etc/nginx/sites-available/default

find the following line:

server_name localhost;

change to your domain name

server_name example.com www.example.com;

reload your nginx new configuration

$ sudo nginx -t
$ sudo service nginx reload

Step 2 Obtaining an SSL Certificate

$ sudo certbot --nginx -d example.com -d www.example.com

You need to configure your HTTPS settings according to certbot.

After you configure your HTTPS, stop the nginx

$ sudo service nginx stop

Step 3 Map your host letsencrypt folder to nginx in docker

You’d like to modify your nignx config file first.

server {
  listen 443 ssl http2;
  server_name app1;

  ssl_certificate /etc/letsencrypt/live/app1.com/fullchain.pem;
  ssl_certificate_key /etc/letsencrypt/live/app1.com/privkey.pem;

  # ... The rest of your NGINX configuration.
}

server {
  listen 80;
  server_name app1.com;
  return 301 https://$host$uri; # redirect http to https
}

Then you need to open 443 port and map folders to docker.

Here is a sample.

version: '2'
services:
   nginx:
    image: nginx:latest
    ports:
        - '80:80'
        - '443:443'  # don't forget to open 443 port
    volumes:
        - ./nginx:/etc/nginx/conf.d
        - ./logs/nginx:/var/log/nginx
        - ./wordpress:/var/www/html
        - /etc/letsencrypt:/etc/letsencrypt # you need to map this folder
        - /etc/ssl:/etc/ssl # you need to map this folder as well
    links:
        - wordpress
    restart: always
    container_name: qinjingfei_nginx

Conclusion

I knew this approach is not elegant, but it works.

References

如何免费的让网站启用HTTPS

How To Secure Nginx with Let’s Encrypt on Ubuntu 14.04

Enabling SSL with Let’s Encrypt, NGINX and Docker

stackoverflow

函数

  • 函数表达式不用于函数声明。函数声明要求有名字,但函数表达式不需要。没有名字的函数表达式也叫作匿名函数。

函数声明的重要特性式函数声明提升(function declaration hoisting).

sayHi();  


//函数声明 和 函数声明提升
function sayHi(){
  alert("Hi");
}

//函数表达式
var functionName = function(arg0, arg1, arg2){

}

  • 递归函数应该始终使用arguments.callee来递归的调用自身,不要使用函数名–函数名可能会变化
function factorial(num){
  if(num <= 1){
    return 1
  }else{
    return num * factorial(num - 1)
  }
}

var anotherFactorail = factorial;
factorial = null;
alert(anotherFactorail(4)) //出错

以上代码把factorial() 函数保存在一个变量anotherFactorail中,再将factorial 变量设置为null. 但在调用anotherFactorail()时,由于需要执行factorial(),而 factorial不再是函数,所以会报错。

正确的做法

function factorial(num){
  if(num <= 1){
    return 1
  }else{
    return num * arguments.callee(num - 1)
  }
}

arguments.callee 是一个指向正在执行的函数的指针,因此可以用它来实现对函数的递归调用。

闭包(closure)

请看我的这篇文章

当函数内部定义了其它函数时,就创建了闭包。闭包有权访问包含函数内部的所有变量,原理如下

  • 在后台执行环境中,闭包的作用域链包含着他自己的作用域、包含函数的作用域和全局作用域
  • 通常,函数的作用域及其所有变量都会在函数执行结束后被销毁

  • 但是,当函数返回一个闭包时,这个函数的作用域会一直在内存中保存闭包不存在为止

闭包可以在javascript 中模仿块级作用域

  • 创建并立即调用一个函数,这样既可以执行其中的代码,又不会在内存中留下对函数的引用。
  • 结果就是函数内部的所有变量都会被立即销毁--除非将某些变量赋值给了包含作用域(即外部作用域)中的变量
(function (){
  //这里是一个块级作用域(block scope)
  })();


function outoutNumbers(count){
  (function(){
    for(var i = 0; i < count; i++){
      alert(i);
    }
    })();

    alert(i); //导致错误
}


  • 闭包还可以用于在对象中创建私有变量

  • JavaScript中没有正式的私有对象属性的概念,但可以使用闭包来实现公有方法,而通过公有方法可以访问在包含作用域中定义的变量

  • 有权访问私有变量的公有方法叫做特权方法 (Privileged Method)

  • 可以使用构造函数模式,原型模式来实现自定义类型的特权方法,也可以使用模块模式,增强的模块模式来实现单例的特权方法。

function MyObejct(){

  //私有变量和函数
  var privateVariable = 10;

  function privateFunction(){
    return false;
  }

  //特权方法
  this.publicMethod = function (){
    privateVariable++;
    return privateFunction();
  }
}

具体请看javascript高级程序设计 P187

JavaScript 中的函数表达式和闭包都是极其有用的特性,可以利用它们来实现很多功能。不过,因为创建闭包必须维护额外的作用域,所以过度使用它们可能会占用大量内存。

LeetCode 26 Remove Duplicates from Sorted Array

Given a sorted array, remove the duplicates in place such that each element appear only once and return the new length.
Do not allocate extra space for another array, you must do this in place with constant memory.
For example, Given input array nums = [1,1,2],
Your function should return length = 2, with the first two elements of nums being 1 and 2 respectively. It doesn’t matter what you leave beyond the new length.

思路

  • 用count来记录有多少个不重复的元素
  • 遇到重复的元素跳过
/**
 * @param {number[]} nums
 * @return {number}
 */
var removeDuplicates = function(nums) {
    if(nums == null || nums.length == 0) return 0;
    if(nums.length == 1) return 1;
    var count = 0;
    for(var i = 1 ; i < nums.length ; i++){
        if(nums[count] != nums[i]){
            count++;
            nums[count] = nums[i];
        }
    }    
    return ++count;
};

引用类型

此文章是总结javascript高级程序设计第5章内容

Object 数据类型

  • object 是一个基础类型,其他所有类型都从object继承基本行为

创建Object实例的两种方法

// new 和Object 构造函数
var person = new Object() 
person.name = "Nicholas";
person.age = 29;
//对象字面量
var person = {
    name: "Nicholas",
    age : 29
};

通过对象字面量定义对象时,不会调用Object构造函数

Array 类型

  • ECMAScript 数组每一项可以保存任何类型的数据
  • 数组大小可以动态调整

创建数组的方式

// new 和 Array 构造函数
var colors = new Array ()

//数组字面量
var colors = ["red","blue","green"];

与对象一样,在使用数组字面量时,不会调用Array构造函数

转换方法

  • 所有对象都具有toLocalString(), toString()和valueOf()方法
  • toString() 会返回有数组中每个值得字符串形式拼接而成的一个以逗号分隔得字符串

  • valueOf() 返回的还是数组
var colors = ["red","blue","green"];
alert(colors.toString()) //"red,blue,green"
alert(colors.valueOf()) //["red","blue","green"]

栈方法(LIFO)

  • push()
  • pop()
var colors = ["red","blue"];
colors.push("brown"); //添加一项
colors[3] = "black"; //添加一项
alert(colors.length); //4

var item = colors.pop();
alert(item); //"black"

队列方法(FIFO)

  • shift()
  • unshift()
  • push()

用shift()和push(),可以像使用队列一样使用数组

var colors = new Array(); // 创建一个数组
var count = colors.push("red","green"); //推入两项
alert(count); //2

count = colors.push("black"); //推入另一项
alert(count); //3

var item = colors.shift(); //取得第一项
alert(item);  // 'red'
alert(colors.length) //2

unshfit()和shift()刚好相反

var colors = new Array();
var count = colors.unshitf("red","green"); //推入两项
alert(count); //2

count = colors.unshift("black"); //推入另一项
alert(count); //3

var item = colors.pop();  //取得最后一项
alert(item); //"green"
alert(colors.length); //2

重排序方法

  • reverse()
  • sort()
//reverse()
var values = [1,2,3,4,5]
alert(values.reverse()); //5,4,3,2,1

// sort()
var values = [0,1,5,10,15];
alert(values.sort()); //0,1,10,15,5

sort()方法比较的是字符串
在进行字符串比较时,10位于5前面

我们可以pass一个函数

var values = [0,1,5,10,15]
alert(values.sort((a,b)=>{
    return a > b
})); // 0,1,5,10,15

操作方法

  • concat()
  • slice()
//concat()
var colors = ["red","green","blue"];
var colors2 = colors.concat("yellow",["black","brown"]);

alert(colors);  //red, green, blue
alert(colors2); //red,green,blue,yellow,black,brown

slice()接受一或两个参数,即要返回项的起始和结束位置(end 不包括)。在只有一个参数的情况下,slice()返回从该参数指定位置开始到当前数组末尾的所有项。

注意,slice()方法不会影响原始数组

var colors = ["red","green","blue","yellow","purple"];
var colors2 = colors.slice(1);
var colors3 = colors.slice(1,4);

alert(color2);  //green,blue,yellow,purple
alert(color3);  //green,blue,yellow

变量、作用域和内存问题

这篇文章简单的总结javascirpt 高级程序设计的第四章。

变量的复制

  • 基本数据类型变量的复制,会创造一个副本
var num1 = 5;
var num2 = num1;

num2 是num1 的一个副本,以后这两个变量可以参与任何操作而不会互相影响。

  • 引用类型变量的复制,复制的是指针,因此两个变量指向同一个变量
var obj1 = new Object();
var obj2 = obj1;
obj1.name = "Nicholas"
alter(obj2.name); //"Nicholas"


首先,变量obj1保存了一个对象的实例。然后,这个值(指针)被复制到了obj2. 所以,obj1和obj2指向同一个对象。

传递参数

ECMAScript中所有函数都是按值传递。也就是说,把函数外面的值复制给函数内部的参数。

  • 基本数据类型
function addTen(num) {
    num += 10;
    return num;
}

var count = 20;
var result = addTen(count);
alert(count); //20 ,没有变化 
alert(result); //20
  • 引用数据类型
function setName(obj){
    obj.name = "Nicholas;"
}

var person = new Object();
setName (person); 
alert(person.name); //"Nicholas"

为了证明对象是按值传递的,请看以下代码

function setName(obj){
    obj.name = "Nicholas;"
    obj = new Object();
    obj.name = "Greg";
}

var person = new Object();
setName (person); 
alert(person.name); //"Nicholas"

如何person 是按引用传递的,那么person 就会自动被修改为指向其name属性值为’Greg’ 的新对象。

检测类型

  • 基本数据类型(typeof)
var s = "Nicholas";
var b = true;
var i = 22;
var u;
var n = null;
var o = new Object();

typeof s; //string
typeof b; //boolean
typeof i; //number
typeof u; //undefined
typeof n; //object
typeof 0; //object

注意typeof 不是函数,是操作符(operator)

  • 引用数据类型(instanceof)
person instanceof Object; // person 是Object 嘛?
colors instanceof Array; // color 是 Array嘛?
pattern instanceof RegExp //pattern 是RegExp 吗

执行环境和作用域

var color = "blue";

function changeColor(){
    var anotherColor = "red";

    function swapColors(){
    var tempColor = anotherColor;
    anotherColor = color;
    color = tempColor;

    //这里可以访问color, anotherColor 和tempColor
    }
    // 这里可以访问color 和anotherColor,但不能访问tempColor
    swapColors();
}
//这里只能访问color
changeColor();

没有块级作用域

在C的语言中,由花括号封闭的代码块有自己的作用域,但在javascript中没有

if(ture){
    var color = "blue";
}

alert(color); //blue

for(var i = 0; i < 10; i++){
    doSomething(i);
}

alert(i); //10

对于javascript来说, 由for语句创建的变量i即使在for循环执行结束后,也依然会存在循环外部的执行环境中

声明变量

  • 使用var 声明的变量会自动被添加最接近的环境中
    • 在函数内部,最接近的环境就是函数的局部环境
  • 如果没有使用var声明,该变量会自动被添加到全局环境

查询标识符

  • 搜索过程从作用域链的前端开始,向上逐级查询与给定名字匹配的标识符。
var color  = "blue" ;
function getColor(){
    return color;
}
alert(getColor()); //"blue"

在这个过程中,如果存在一个局部变量的定义,则搜过会自动停止。

LeetCode 21 Merge Two Sorted Lists

Merge two sorted linked lists and return it as a new list. The new list should be made by splicing together the nodes of the first two lists.

/**
 * Definition for singly-linked list.
 * function ListNode(val) {
 *     this.val = val;
 *     this.next = null;
 * }
 */
/**
 * @param {ListNode} l1
 * @param {ListNode} l2
 * @return {ListNode}
 */
var mergeTwoLists = function(l1, l2) {
    var ans  = []
    while(l1 != null){
        ans.push(new ListNode(l1.val));//cut ListNode and push into an array
        l1 = l1.next;
    }
    while(l2 != null){
        ans.push(new ListNode(l2.val)); //cut ListNode and push into an array
        l2 = l2.next
    }

    ans.sort(function(a, b){  //sort the array
        return a.val - b.val;
    })

    if(ans.length==0) return null;
    for(var i = 0; i < ans.length-1; i++){   
        ans[i].next = ans[i+1]
    }

    return ans[0]
};

LeetCode 3 Longest Substring Without Repeating Characters

Given a string, find the length of the longest substring without repeating characters.

Examples:

Given “abcabcbb”, the answer is “abc”, which the length is 3.

Given “bbbbb”, the answer is “b”, with the length of 1.

Given “pwwkew”, the answer is “wke”, with the length of 3. Note that the answer must be a substring, “pwke” is a subsequence and not a substring.

求最长非重复子串

比如 “abcabcbb” => “abc” =>长度为3
比如 “bbbbb” => “b” => 长度为1
比如 “pwwkew” => “wke” =>长度为3


/** * @param {string} s * @return {number} */ var lengthOfLongestSubstring = function(s) { var hash = {}; var start = 0; var ans = 0; for (var i = 0, len = s.length; i < len; i++) { var item = s[i]; if (!hash[item]) // item 不在substring中 hash[item] = true; else { // item 已经在 substring 中存在了 for (; 😉 { if (s[start] === item) { // 开头重复 start++; break; } hash[s[start]] = false; //非开头重复 start++; } }// 结束 for loop ans = Math.max(ans, i - start + 1); } return ans; };

LeetCode 7 Reverse Integer

Reverse digits of an integer.

Example1: x = 123, return 321
Example2: x = -123, return -321

Have you thought about this?

Here are some good questions to ask before coding. Bonus points for you if you have already thought through this!

If the integer’s last digit is 0, what should the output be? ie, cases such as 10, 100.

Did you notice that the reversed integer might overflow? Assume the input is a 32-bit integer, then the reverse of 1000000003 overflows. How should you handle such cases?

For the purpose of this problem, assume that your function returns 0 when the reversed integer overflows.

Note:
The input is assumed to be a 32-bit signed integer. Your function should return 0 when the reversed integer overflows.

解题思路

首先想到需要3种情况: 大于0和小于0的情况,可以用sign变量保存符号,然后在reverse。注意的是,overflow是在reverse完之后判断,因为input默认是没有overflow的.

/**
 * @param {number} x
 * @return {number}
 */
var reverse = function(x){
    var sign= (x>0)?1: -1;
    x=Math.abs(x);
    var str=x.toString().split("").reverse().join("");
    var result=sign * Number(str);
    if(result > 2147483647 || result < -2147483648) return 0; // reverse 完之后判断
    else return result;
};

缺点是因为用了很多javascript built-in 函数,不适用别的语言

LeetCode 1 Two Sum

Given an array of integers, return indices of the two numbers such that they add up to a specific target.

You may assume that each input would have exactly one solution, and you may not use the same element twice.

Example

Given nums = [2, 7, 11, 15], target = 9,
Because nums[0] + nums[1] = 2 + 7 = 9,
return [0, 1].

解题思路

刚开始感觉遍历所有两个元素的组合来判断是否等于目标值,需要n∗(n−1)/2 步。(O(n^2))

第一次尝试

/**
 * @param {number[]} nums
 * @param {number} target
 * @return {number[]}
 */
var twoSum = function(nums, target) {
  for(var i = 0; i < nums.length; i++)
    for(var j = i; j < nums.length; j++)
      if(nums[i] + nums[j] == target)
        return [i,j]
};

测试

// Given nums = [2, 7, 11, 15], target = 9
[0,1]
// Given nums = [2, 8, 11, 15], target = 9
undefined

看起来这种方法应该符合条件,但其实不符合题目,题目说每个元素只能用一次,很明显第一种方法不符合题目(每个元素用了n-1次)

第二次尝试

题目要求每个元素只能用一次,那就意味着大O是O(n). 什么样的的算法可以遍历一遍所有元素判断出来呢?虽然,我想到了应该用map(hash table) 来写,但我对map不熟,看到别人的算法才明白该怎么做。

/**
 * @param {number[]} nums
 * @param {number} target
 * @return {number[]}
 */
var twoSum = function(nums, target) {
  var map = {}
  for(var i = 0; i < nums.length; i++){
    var v = nums[i]
    if(map[target - v] >= 0)   //判断target-v是否在map出现过(所有index都是大于等于0的数)
      return [map[target - v], i]
    else map[v]=i //用map储存数字和位置
  }
}

测试

// Given nums = [2, 7, 11, 15], target = 9
[0,1]
// Given nums = [2, 8, 11, 15], target = 9
undefined

这道题的解题关键是对hash table有很好的理解,切入点是每个元素只能用一次 ===> O(n). O(n) 意味着只能用一次for循环和大O是常数的算法,也只剩下hash table 能做到O(1)的search了。

RESTful API

介绍

RESTful(REpresentational State Transfer) 直接翻译是: 表现层状态转移。它是一种统一的机制,方便前后端通信。

域名

尽量把API布置在专用域名之下

https://api.example.com

如果API很简单,可以放到主域名下.

https://example.org/api/

版本

应该把API的版本号放入URL

https://api.example.com/v1/

路径

在RESTful 构架中,每个网址代表一种资源(resource).
URL定位资源,用HTTP动词(GET,POST,DELETE,DETC)描述操作。

  • 网址不能有动词, 只能是名词
  • 数据库中的表都是同种记录的集合,所以应该用复数
https://api.example.com/v1/zoos
https://api.example.com/v1/animals
https://api.example.com/v1/employees

HTTP 动词

  • GET(SELECT): 从服务器中取出资源
  • POST(CREATE): 在服务器新建一个资源
  • PUT(UPDATE): 在服务器更新资源
  • PATCH(UPDATE):在服务器更新资源(改变属性)
  • DELETE (DELETE): 在服务器删除资源

还有两个不常用的HTTP 动词

  • HEAD: 获取资源的元数据
  • OPTIONS: 获取信息,关于资源的属性是客户端可以改变的

过滤信息

常见的参数

?limit=10:指定返回记录的数量
?offset=10:指定返回记录的开始位置。
?page=2&per_page=100:指定第几页,以及每页的记录数。
?sortby=name&order=asc:指定返回结果按照哪个属性排序,以及排序顺序。
?animal_type_id=1:指定筛选条件

状态码

200 OK – [GET]:服务器成功返回用户请求的数据,该操作是幂等的(Idempotent)。
201 CREATED – [POST/PUT/PATCH]:用户新建或修改数据成功。
202 Accepted – :表示一个请求已经进入后台排队(异步任务)
204 NO CONTENT – [DELETE]:用户删除数据成功。
400 INVALID REQUEST – [POST/PUT/PATCH]:用户发出的请求有错误,服务器没有进行新建或修改数据的操作,该操作是幂等的。
401 Unauthorized – :表示用户没有权限(令牌、用户名、密码错误)。
403 Forbidden – 表示用户得到授权(与401错误相对),但是访问是被禁止的。
404 NOT FOUND – :用户发出的请求针对的是不存在的记录,服务器没有进行操作,该操作是幂等的。
406 Not Acceptable – [GET]:用户请求的格式不可得(比如用户请求JSON格式,但是只有XML格式)。
410 Gone -[GET]:用户请求的资源被永久删除,且不会再得到的。
422 Unprocesable entity – [POST/PUT/PATCH] 当创建一个对象时,发生一个验证错误。
500 INTERNAL SERVER ERROR -:服务器发生错误,用户将无法判断发出的请求是否成功。