Archive of September 2020

Seafile server configuration on CentOS 8

I am using the docker-compose version. The first problem I met was none of Seafile's concerns, it is actually a bug of docker. All of the containers cannot reach the internet (cannot resolve any DNS). The solution is at here.

After fixed the issue above, the Seafile server runs well on centos. However, there are still two issues that need to be addressed.

The first one is about the head icon of a user. It simply cannot display. You can easily found that the host part of the head icon is wrong. You need to edit "shared/seafile/conf/ccnet.conf" and change the SERVICE_URL to your real hostname in the [General] section.

The second one is similar, if you don't fix it, you cannot download any of the file you uploaded. This time, you need to edit "shared/seafile/conf/seahub_settings.py" and change the last line to "https://your.domain.name/seafhttp".

tmux super simple guide

echo 'set -g mouse on' >> ~/.tmux.conf
tmux new -s tmux3
^b %
^b "
^b d
tmux attach -t tmux3

Superpay 小程序支付后端接口对接记

这两天在和开发小程序的同事对接supay支付,今天总算基本搞完了。他们的文档过于糟糕,太多槽要吐,记录一下也许会帮到其他也需要对接superpay的人。先说说我们的业务流程

  1. 小程序 wx.login 拿到 code
  2. 小程序 wx.getUserInfo 拿到加密的 userData和iv
  3. 小程序登录请求公司后端服务器 /wechatMiniProgram/login 接口三方登录,登录成功拿到可访问公司其他接口的 accessToken
  4. 小程序拿着accessToken下订单
  5. 小程序拿着订单id请求公司后端服务器 /supay/wechatMiniProgramPay/{orderId}
  6. 公司服务器请求 supay 小程序下单接口,获取pay_info 给小程序。故事主要发生在这里。

他们的文档里是有一个流程图的,写成文本大体是下面这样。

  1. 小程序向公司后台服务器发起下单支付请求
  2. 公司后台服务器请求 superpay 服务器获取 openid
  3. superpay 服务器返回 openid 给公司后台服务器
  4. 公司后台服务器拿着openid 请求superpay服务器(小程序下单接口)拿 pay_info
  5. 公司服务器把 pay_info 返回给小程序
  6. 小程序拿 pay_info 发起支付

这个流程图事后看其实也不算错,只是其中第2步和第3步是不必要的。因为小程序在三方登录的时候,我们已经可以拿到openid和unionid。补充一句,小程序支付只需要openid,并不需要unionid。

在和superpay的技术同事沟通过程中,我发现最有价值的聊天记录包括

不要管其他的接口了,就用小程序下单
交易状态 TRADE_FINISHED 不需要处理
小程序下单返回的接口数据中,所有时间均是北京时间
inner_trade_no是 supay 的单号,mch_order_no和outer_trade_no 是你们的单号
trade_amount是人民币金额,虽然例子里写的是纽币
签名的时候字典序是重要的,文档里虽然没有提到,但在签名串字典序末尾追加一个 &key=商户appsecret 再md5是至关重要的。

因为签名需要拼一个query string格式的串,我就想当然的认为是post这个串到supay服务器。这是我的错,这里要以例子为准。例子里写的就是post一个json格式的数据过去。

10.07.2020 补充一个吐槽,小程序返回的 trade_amount 单位并不是人民币,是纽币。而且小程序各接口没有一个能返回人民币实际支付额。我的解决办法是拿自己的订单号调用 superpay 给非小程序支付的查询交易详情接口,这样就能拿到人民币交易金额数据。

How to setup pm2 to automatically start after reboot?

$ pm2 start
$ pm2 save
$ pm2 startup

You will see something like the following after you run the last command:

[PM2] Init System found: systemd
[PM2] To setup the Startup Script, copy/paste the following command:
sudo env PATH=$PATH:/usr/local/lib/nodejs/node-v12.18.0-linux-x64/bin /usr/local/lib/nodejs/node-v12.18.0-linux-x64/lib/node_modules/pm2/bin/pm2 startup systemd -u shukebeta --hp /home/shukebeta

copy the sudo line & paste it on your command line then press Enter! You have done.

Here's a sample pm2.config.json for a project.

{
  "apps" : [{
    "name": "tracking-system",
    "script": "app.js",
    "watch": ".",
    "exec_mode" : "cluster",
    "instances"  : 4,
    "exec_mode"  : "cluster",
    "autorestart": true
  } ]
}

JavaScript中七种函数调用方式及对应 this 的含义

旧文重发存档,原文发表于9年前。原文地址

this 在 JavaScript 开发中占有相当重要的地位,不过很多人对this这个东西都感觉到琢磨不透。要真正理解JavaScript的函数机制,就非常有必要搞清楚this到底是怎么回事。

函数调用方式不同,this 含义也跟着不同。JavaScript语言中有七种调用函数方式:

第一种:调用方法

var obj = {
    method: function() { alert(this === obj); }
}
obj.method();

上面这行obj.method()显然method是作为方法被调用,这种情况下,函数体中的this绑定的就是method的宿主对象,也就是obj。 从这种调用方式我们得出第一定律: 第一定律:以方法方式调用函数,this则绑定宿主对象。

第二种:调用全局函数

var method = function(){alert(this === window);}
method();

上面这个函数是个全局函数。我们知道,全局变量或函数都相当于window对象的属性。也就是说,上面这句实际上等同于下面这句:

window.method = function(){alert(this === window);}
window.method();

既然这样,那么这里 this 绑定到 window 对象就显而易见,很容易理解了。相当于方法调用模式的一个特例,并不违背第一定律。

第三种:全局函数内调用内部函数

var m_ext = function() {
    alert(this === window);
    var m_inner = function() {
       alert(this === window);
    }
   m_inner();
}
m_ext();

执行上面这段代码,你会惊讶的发现,两个布尔表达式的值都是真。也就是说子函数孙函数,只要是以函数的方式调用,this 就铁了心绑定 window 对象了。从这种调用方式我们得出第二定律: 第二定律:不论子函数孙函数,只要是以函数的方式调用,this 就铁了心绑定 window 对象。

第四种:方法内调用内部函数

var obj = {};
obj.method = function() {
    alert(this === obj);
    var m_inner = function() {
       alert(this === window);
    }
   m_inner();
}
obj.method();

执行上面这段代码,第一个this绑定到obj你不奇怪,第二个this绑定到window其实也不奇怪。它仍然遵守第二定律,也就是不论子函数孙函数,只要是以函数的方式调用,this 就铁了心绑定 window 对象。

第五种:作为构造函数调用

function Person(name, age){
    this.name = name;
    this.age = age;
}
var john = new Person('John', 38);
alert(JSON.stringify(john));

你会说,哇,这很眼熟。生成某个类的新对象啊。Javascript就是这么另类,函数确实可以这么调。 这次弹出的是这样一个字符串:{"name":"John","age":38}。哇,我明明没在函数定义里写return 语句,它却主动返回了一个对象给我。没错,即便你在函数定义里return某个东西,它也不会理你。(注意:如果你return一个function就会有惊喜,不信你试试看)。从这种调用方式我们得出第三定律:

第三定律:如果在一个函数本身没有返回值,在其前面加上 new 调用,就会创建一个连接到该函数 prototype 属性的新对象,再把 this 绑定到该对象,然后执行该函数,最后返回该对象。如果该函数有返回值,且返回值为字符串/数字/布尔值等简单对象的话,该返回值会被丢弃。但如果该函数的返回值为对象,函数或者数组等复杂对象的话,该函数则会返回该返回值,抛弃this绑定的对象。据我测试,如果返回值是一个函数时,该函数会返回undefined。我暂时还不知道为什么会这样。

第六种:用函数对象的apply方法调用

var my_concat = function(stra, strb){
   alert(this);
    return stra + '' + strb;
}
var param = ['hello', ' world']
alert(my_concat.apply('bullshit', param));

你一定注意了,在这里 this 绑定的是apply方法的第一个参数 'bullshit'。从这里我们得出第四定律: 第四定律:用方法的apply/call方法调用方法时,this 则被绑定到apply/call方法的第一个参数。 想谁就是谁,嗯,吴妈也行。JavaScript的程序员们好幸福哦。

第七种:用函数对象的call方法调用

var my_concat = function(stra, strb){
   alert(this);
    return stra + '' + strb;
}
alert(my_concat.call('bullshit', 'hello',' world'));

对啦,你或许会多问一句,apply 的第二个参数有什么规定?call方法和apply又有什么不同?嗯 ,真是勤学好问,我就再啰嗦一下说说apply和call:

apply 和 call 的区别:

apply要求第二个参数必须是数组。否则就会报 TypeError: second argument to Function.prototype.apply must be an array. 而call则没这么严格啦,第二个参数要什么类型?随意啦,还可以有第三个第四个第五个第六个....啦。

我想我说的够清楚了。感谢《JavaScript The Good Parts》和《JavaScript Definitive Guide》,要不然我也弄不明白呢!