统计代码行数
find  . \
\( -path ./web -o -path ./runtime -o -path ./config \) -prune -o \
\( -name "*.php" -o -name "*.js" -o -name "*.css" -o -name "*.scss" \) \
| xargs wc -lweb,runtime,config为需要排除的目录
php,js,css,scss为源码文件扩展名
统计代码行数
find  . \
\( -path ./web -o -path ./runtime -o -path ./config \) -prune -o \
\( -name "*.php" -o -name "*.js" -o -name "*.css" -o -name "*.scss" \) \
| xargs wc -lweb,runtime,config为需要排除的目录
php,js,css,scss为源码文件扩展名
Disable hyper-v (which will required a couple of restarts)
dism.exe /Online /Disable-Feature:Microsoft-Hyper-VWhen you finish all the required restarts, reserve the port you want so hyper-v doesn’t reserve it back
netsh int ipv4 add excludedportrange protocol=tcp startport=50051  numberofports=1Re-Enable hyper-V (which will require a couple of restart)
dism.exe /Online /Enable-Feature:Microsoft-Hyper-V /Allwhen your system is back, you will be able to bind to that port successfully.
打开Windows Terminal,点击设置,可以看到,复制增加一组ssh配置,然后修改三个地方。
{
// Make changes here to the powershell.exe profile.
"guid": "{61c54bbd-c2c6-5271-96e7-009a87ff44bf}",
"name": "Windows PowerShell",
"commandline": "powershell.exe",
"hidden": false
},利用powershell可以生成guid
powershell -Command "[guid]::NewGuid().ToString()"任意,可以是系统名字,IP,用户名等,如 username@aliyun-bj
ssh -i C:\user\username\id_rsa username@aliyun-bj其中i参数后面为证书存放位置,如果试用密码登录可以不填
 {
     "acrylicOpacity" : 1,
     "closeOnExit" : true,
     "colorScheme" : "One Half Dark",
     "commandline" : "ssh username@aliyun-bj",
     "cursorColor" : "#FFFFFF",
     "cursorShape" : "bar",
     "fontFace" : "Consolas",
     "fontSize" : 14,
     "guid" : "{61c54bbd-c2c6-5271-96e7-009a87ff44bf}",
     "historySize" : 9001,
     "icon" : "ms-appx:///ProfileIcons/{9acb9455-ca41-5af7-950f-6bca1bc9722f}.png",
     "name" : "username@aliyun-bj",
     "padding" : "0, 0, 0, 0",
     "snapOnInput" : true,
     "startingDirectory" : "",
     "useAcrylic" : false
 }{
            "guid": "{7f0152b2-c491-4bb6-8a87-610352d630c8}",
            "acrylicOpacity" :1,
            "closeOnExit" : true,
            "colorScheme" : "Campbell",
            "commandline" : "\"%PROGRAMFILES%\\Git\\Bin\\bash.exe\" -i -l",
            "cursorColor" : "#FFFFFF",
            "cursorShape" : "bar",
            "fontFace" : "Consolas",
            "fontSize" : 14,
            "historySize" : 9001,
            "icon" : "%PROGRAMFILES%\\Git\\mingw64\\share\\git\\git-for-windows.ico",
            "name" : "Git-bash",
            "padding" : "0, 0, 0, 0",
            "snapOnInput" : true,
            "startingDirectory" : "%USERPROFILE%",
            "useAcrylic" : true
        }        {
            "guid": "{2c4de342-38b7-51cf-b940-2309a097f518}",
            "hidden": false,
            "name": "Ubuntu",
            "source": "Windows.Terminal.Wsl"
        }, {
                "guid": "{17da3cac-b318-431e-8a3e-7fcdefe6d114}",
                "name": "MINGW64 / MSYS2",
                "commandline": "C:/msys64/msys2_shell.cmd -defterm -here -no-start -mingw64",
                "startingDirectory": "C:/msys64/home/%USERNAME%",
                "icon": "C:/msys64/mingw64.ico",
                "fontFace": "Lucida Console",
                "fontSize": 9
            },
            {
                "guid": "{2d51fdc4-a03b-4efe-81bc-722b7f6f3820}",
                "name": "MINGW32 / MSYS2",
                "commandline": "C:/msys64/msys2_shell.cmd -defterm -here -no-start -mingw32",
                "startingDirectory": "C:/msys64/home/%USERNAME%",
                "icon": "C:/msys64/mingw32.ico",
                "fontFace": "Lucida Console",
                "fontSize": 9
            },
            {
                "guid": "{71160544-14d8-4194-af25-d05feeac7233}",
                "name": "MSYS / MSYS2",
                "commandline": "C:/msys64/msys2_shell.cmd -defterm -here -no-start -msys",
                "startingDirectory": "C:/msys64/home/%USERNAME%",
                "icon": "C:/msys64/msys2.ico",
                "fontFace": "Lucida Console",
                "fontSize": 9
            }比较了网易七鱼,创逸云客服,udesk,单小二,腾讯云智服,售后宝,以及国内开源产品。
收费软件大多是集成在客服、CRM中,存在接口不足、界面太重、价格过高的问题。开源项目都基本是没法用。而国外的对微信,钉钉支付不好。
目前看来售后宝比较符合需求。API调用10万次1000元。

if ($host ~ ^(?!www\.)(?<domain>.+)$) {
    return  301 $scheme://www.$domain$request_uri;
}if ($host ~ ^www\.(?<domain>.+)$) {
    return  301 $scheme://$domain$request_uri;
}if ($host != "example.com") {
    return  301 $scheme://example.com$request_uri;
}if ($scheme != "https") {
       return 301 https://$host$request_uri;
   } # managed by Certbot最近收到反馈网站公众号H5打卡慢,发现都是iOS用户,最后搜了以下发现可能因为letsencrypt证书域名被污染,导致到国外验证证书慢,解决办法是开启OCSP Stapling本地服务器缓存。
nginx 日志可以看到
ocsp.int-x3.letsencrypt.org could not be resolved (110: Operation timed out)openssl s_client -connect c4ys.com:443 -servername c4ys.com -status -tlsextdebug < /dev/null 2>&1 | grep "OCSP"(nginx重启后需要运行两次,第二次才有结果)
如果结果是下面这样,说明 OCSP Stapling 已开启:
OCSP response:
OCSP Response Data:
    OCSP Response Status: successful (0x0)
    Response Type: Basic OCSP Response而这样显然是未开启:
OCSP response: no response sent根据 nginx ssl_stapling 官方文档只需要两个条件即可:
letsencrypt certbot的ssl_certificate使用的是fullchain.pem,带了中间证书,所以只需要指定resolver即可。生成ssl_trusted_certificate和ssl_stapling_file的步骤不是必须的。
ssl_stapling on;
ssl_stapling_verify on;
resolver 8.8.8.8 8.8.4.4 valid=60s ipv6=off;
resolver_timeout 5s;查看站点根证书,0,站点证书;1,中间证书,2,根证书
openssl s_client -connect c4ys.com:443 -servername c4ys.com -showcerts < /dev/null 2>&1
depth=2 O = Digital Signature Trust Co., CN = DST Root CA X3
verify return:1
depth=1 C = US, O = Let's Encrypt, CN = Let's Encrypt Authority X3
verify return:1
depth=0 CN = c4ys.com
verify return:1cd /etc/letsencrypt/live/c4ys.com/
wget https://letsencrypt.org/certs/trustid-x3-root.pem.txt
mv trustid-x3-root.pem.txt trustid-x3-root.pemopenssl x509 -in cert.pem -noout -subject
openssl x509 -in chain.pem -noout -subject
openssl x509 -in trustid-x3-root.pem -noout -subjectcat cert.pem chain.pem  trustid-x3-root.pem > ca_bundled.pem查看证书签署,来自ocsp.int-x3.letsencrypt.org
openssl x509 -in cert.pem -noout -ocsp_uriopenssl ocsp -no_nonce -text \
-issuer chain.pem \
-cert cert.pem \
-CAfile ca_bundled.pem \
-VAfile ca_bundled.pem \
-url http://ocsp.int-x3.letsencrypt.org/ \
-header "HOST" "ocsp.int-x3.letsencrypt.org"发现超时,修改/etc/resolv.conf,服务器dns
nameserver 8.8.8.8然后出现
Response verify OK
cert.pem: goodssl_trusted_certificate /etc/letsencrypt/live/c4ys.com/ca_bundled.pem;开启后可以通过前面的方法验证,第二次才有效
服务器可以将OSCP resp缓存起来,避免走国外下载
由于ssl_stapling_file需要手动更新,比较麻烦,所以通过http代理获取。
参考:https://jhuo.ca/post/ocsp-stapling-letsencrypt/
ssl_stapling_file可以将oscp缓存起来,避免服务器下载
openssl ocsp -no_nonce -text \
-issuer chain.pem \
-cert cert.pem \
-CAfile ca_bundled.pem \
-VAfile ca_bundled.pem \
-url http://ocsp.int-x3.letsencrypt.org/ \
-header "HOST" "ocsp.int-x3.letsencrypt.org" \
-respout ocsp.respssl_stapling_file /etc/letsencrypt/live/c4ys.com/ocsp.resp;oscp有效时间通常只有7天,所以需要使用脚本自动更新缓存,参考:https://quchao.com/entry/how-to-configure-ocsp-stapling-on-nginx-for-the-certificates-issued-by-lets-encrypt/
id 
name 姓名id
family_id 族号 一个用户可能存在多个族谱中,比如外嫁后进入其他族谱
people_id 用户编号
level 代系
relate_people_id 关联人,
type 关系:如丈夫,妻子,儿子,孙子,情人
order 第几个孩子,第几任妻子alter table relation create index family_level(family_id,level);select * from relation where family_id=3 and level between current_level-3,current_level+3;这两个表记录基本不用改(除非录错了),任何时候都是增加记录即可,所以维护也非常简单。
界面展示上,查询出上下几代,然后名字作为主要展示标签,关系用连线展示,关系和人是多对多,完美解决关系混乱的问题。
level 代表是族谱里面的第几代。
如果发现了新的祖先,然么当然自己代数要加一。
比如李渊原来 level 是 1 代,然后认了老子做祖宗,那么他就不 1 代了,老子是 1 代。

C:\Program Files下新建PhpStorm Protocol (Win)文件夹,包含以下两个文件。
修改run_editor.reg后,点击运行即可。
REGEDIT4
[HKEY_CLASSES_ROOT\phpstorm]
@="URL:phpstorm Protocol"
"URL Protocol"=""
[HKEY_CLASSES_ROOT\phpstorm\shell\open\command]
@="wscript \"C:\\Program Files\\PhpStorm Protocol (Win)\\run_editor.js\" \"%1\" //E:JScript"
[HKEY_CLASSES_ROOT\ide]
@="URL:ide Protocol"
"URL Protocol"=""
[HKEY_CLASSES_ROOT\ide\shell\open\command]
@="wscript \"C:\\Program Files\\PhpStorm Protocol (Win)\\run_editor.js\" \"%1\" //E:JScript"var settings = {
    // flag to active Jetbrain Toolbox configuration
    toolBoxActive: false,
    // Set to 'true' (without quotes) if run on Windows 64bit. Set to 'false' (without quotes) otherwise.
    x64: true,
    // Set to folder name, where PhpStorm was installed to (e.g. 'PhpStorm')
    folder_name: 'PhpStorm 2018.3.1',
    // Set to window title (only text after dash sign), that you see, when switching to running PhpStorm instance
    window_title: 'PhpStorm',
    // In case your file is mapped via a network share and paths do not match.
    // eg. /var/www will can replaced with Y:/
    projects_basepath: '',
    projects_path_alias: ''
};
// don't change anything below this line, unless you know what you're doing
var url = WScript.Arguments(0),
    match = /^phpstorm|ide:\/\/open\/?\?(url=file:\/\/|file=)(.+)&line=(\d+)$/.exec(url),
    project = '',
    editor = '"C:\\' + ( settings.x64 ? 'Program Files' : 'Program Files (x86)' ) + '\\JetBrains\\' + settings.folder_name + ( settings.x64 ? '\\bin\\phpstorm64.exe' : '\\bin\\phpstorm.exe' ) + '"';
if (settings.toolBoxActive) {
    configureToolboxSettings(settings);
}
// WScript.Echo(editor);
// WScript.Echo(url);
if (match) {
    var shell = new ActiveXObject('WScript.Shell'),
        file_system = new ActiveXObject('Scripting.FileSystemObject'),
        file = decodeURIComponent(match[ 2 ]).replace(/\+/g, ' '),
        search_path = file.replace(/\//g, '\\');
    if (settings.projects_basepath !== '' && settings.projects_path_alias !== '') {
        file = file.replace(new RegExp('^' + settings.projects_basepath), settings.projects_path_alias);
    }
    while (search_path.lastIndexOf('\\') !== -1) {
        search_path = search_path.substring(0, search_path.lastIndexOf('\\'));
        if (file_system.FileExists(search_path + '\\.idea\\.name')) {
            project = search_path;
            break;
        }
    }
    if (project !== '') {
        editor += ' "%project%"';
    }
    editor += ' --line %line% "%file%"';
    var command = editor.replace(/%line%/g, match[ 3 ])
        .replace(/%file%/g, file)
        .replace(/%project%/g, project)
        .replace(/\//g, '\\');
    shell.Exec(command);
    shell.AppActivate(settings.window_title);
}
function configureToolboxSettings(settings) {
    var shell = new ActiveXObject('WScript.Shell'),
        appDataLocal = shell.ExpandEnvironmentStrings("%localappdata%"),
        toolboxDirectory = appDataLocal + '\\JetBrains\\Toolbox\\apps\\PhpStorm\\ch-0\\';
    // Reference the FileSystemObject
    var fso = new ActiveXObject('Scripting.FileSystemObject');
    // Reference the Text directory
    var folder = fso.GetFolder(toolboxDirectory);
    // Reference the File collection of the Text directory
    var fileCollection = folder.SubFolders;
    var maxMajor = 0,
        maxMinor = 0,
        maxPatch = 0,
        maxVersionFolder = "";
    // Traverse through the fileCollection using the FOR loop
    // read the maximum version from toolbox filesystem
    for (var objEnum = new Enumerator(fileCollection); !objEnum.atEnd(); objEnum.moveNext()) {
        var folderObject = ( objEnum.item() );
        if (folderObject.Name.lastIndexOf('plugins') === -1) {
            var versionMatch = /(\d+)\.(\d+)\.(\d+)/.exec(folderObject.Name),
                major = parseInt(versionMatch[ 1 ]),
                minor = parseInt(versionMatch[ 2 ]),
                patch = parseInt(versionMatch[ 3 ]);
            if (maxMajor === 0 || maxMajor <= major) {
                maxMajor = major;
                if (maxMinor === 0 || maxMinor <= minor) {
                    maxMinor = minor;
                    if (maxPatch === 0 || maxPatch <= patch) {
                        maxPatch = patch;
                        maxVersionFolder = folderObject.Name;
                    }
                }
            }
        }
    }
    settings.folder_name = maxVersionFolder;
    // read version name and product name from product-info.json
    var versionFile = fso.OpenTextFile(toolboxDirectory + settings.folder_name + "\\product-info.json", 1, true);
    var content = versionFile.ReadAll();
    eval('var productVersion = ' + content + ';');
    settings.window_title = 'PhpStorm ' + productVersion.version;
    editor = '"' + toolboxDirectory + settings.folder_name + '\\' + productVersion.launch[ 0 ].launcherPath.replace(/\//g, '\\') + '"';
}