Gowhich

Durban's Blog

ssl证书获取

免费的渠道 https://certbot.eff.org/

也可以去云平台获取 比如阿里云、腾讯云也有免费的

先说下 ssl/dhparam.pem 这个文件如何获取

如何生成 dhparam.pem 文件

在命令行执行任一方法:

方法1: 很慢

1
openssl dhparam -out /etc/nginx/ssl/dhparam.pem 2048

方法2: 较快 - 与方法1无明显区别. 2048位也足够用, 4096更强

1
openssl dhparam -dsaparam -out /etc/nginx/ssl/dhparam.pem 4096

参考 https://gist.github.com/fotock/9cf9afc2fd0f813828992ebc4fdaad6f

采用七牛的SDK方式上传(qiniu sdk文件可以去官网示例页面下载,放在github上的文件需要自己下载后打包的,很麻烦)

首先看下公用的上传函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
function uploadWithSDK (file, token, putExtra, config, domain) {
var self = this;

var finishedAttr = [];

var compareChunks = [];

var observable;

if (file) {
// var key = file.name;
var key = 'activity/' + (new Date().valueOf()) + '/' + self.uploadKey;
putExtra.params["x:name"] = key.split(".")[0];

// 设置next,error,complete对应的操作,分别处理相应的进度信息,错误信息,以及完成后的操作
var error = function (err) {
console.log('err = ', err);
self.uploading = false;
};

var complete = function (res) {
console.log('res = ', res);
self.uploading = false;
self.uploaded = true;
var url = self.domain + '/' + res.key;
console.log(url);
self.videoUrl = url
};

var next = function (response) {
var chunks = response.chunks || [];
var total = response.total;
console.log('total = ', total);
console.log('chunks = ', chunks);
self.uploadPercent = (total.percent + '').substr(0, 5) + '%';
};

var subObject = {
next: next,
error: error,
complete: complete
};

var subscription;

// 调用sdk上传接口获得相应的observable,控制上传和暂停
observable = qiniu.upload(file, key, token, putExtra, config);

subscription = observable.subscribe(subObject);

// subscription.unsubscribe();
}
}

调用上传视频函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
function uploadVideo(event) {
var file = event.target.files[0];
// this.uploading = true;

console.log(file);
console.log(file.size);

var config = {
useCdnDomain: true,
disableStatisticsReport: false,
retryCount: 6,
region: qiniu.region.z0
};

var putExtra = {
fname: "",
params: {},
mimeType: null
};

uploadWithSDK(file, this.uploadToken, putExtra, config, this.domain);
}

触发file的click事件

1
2
3
function upload() {
this.$refs.fileElem.dispatchEvent(new MouseEvent('click'));
}

从上面代码可以看出, 代码适用于vuejs中,但是触发click的话,只要是js代码都能实现

vuejs中视频上传相关变量初始化

1
2
3
4
5
6
7
8
9
10
data: {
uploadToken: '',
domain: '',
uploadKey: '',
randomKey: '',
uploading: false,
uploaded: false,
videoUrl: '',
uploadPercent: '',
}

vuejs中上传视频隐藏表单的HTML

1
2
3
4
5
6
<!-- 上传图片的隐藏元素 -->
<div style="display:none">
<form id="fileElem1">
<input ref=fileElem type="file" class="file" accept="video/*" name="img" @change="uploadVideo($event)" style="display: none">
</form>
</div>

vuejs中上传视频按钮部分

1
<span @click='upload' style="">上传视频</span>

这里七牛的sdk请去官网下载

想不通直接打包一个不挺好的嘛,自己下载下来非常麻烦。(实例地址 http://jssdk-v2.demo.qiniu.io/)

git-flow 工作流程

当在团队开发中使用版本控制系统时,商定一个统一的工作流程是至关重要的。Git 的确可以在各个方面做很多事情,然而,如果在你的团队中还没有能形成一个特定有效的工作流程,那么混乱就将是不可避免的。

基本上你可以定义一个完全适合你自己项目的工作流程,或者使用一个别人定义好的。

什么是git-flow

一旦安装 git-flow,你将会拥有一些扩展命令。这些命令会在一个预定义的顺序下自动执行多个操作。是的,这就是我们的工作流程!

git-flow 并不是要替代 Git,它仅仅是非常聪明有效地把标准的 Git 命令用脚本组合了起来。

严格来讲,你并不需要安装什么特别的东西就可以使用 git-flow 工作流程。你只需要了解,哪些工作流程是由哪些单独的任务所组成的,并且附带上正确的参数,以及在一个正确的顺序下简单执行那些对应的 Git 命令就可以了。当然,如果你使用 git-flow 脚本就会更加方便了,你就不需要把这些命令和顺序都记在脑子里

安装git-flow

近些年来出现了很多不同的安装方法。在本章节中我们会使用当前最流行的一种: AVH Edition

要了解安装 git-flow 细节,请阅读下面这个文档 official documentation

在项目中设置 git-flow

当你想把你的项目 “切换” 到 git-flow 上后,Git 还是可以像往常一样工作的。这完全是取决于你在仓库上使用特殊的 git-flow 命令或是普通的 Git 命令。换句话说,git-flow 它不会以任何一种戏剧性的方式来改变你的仓库。

话虽如此,git-flow 却存在一些限制。让我们开始在一个新的项目上初始化它吧,之后我们就会有所发现:

1
2
3
4
5
6
7
8
9
$ git flow init
Initialized empty Git repository in /Users/xxx/xxx-website/.git/
Branch name for production releases: [master]
Branch name for "next release" development: [develop]

How to name your supporting branch prefixes?
Feature branches? [feature/]
Release branches? [release/]
Hotfix branches? [hotfix/]

当在项目的根目录执行 “git flow init” 命令时(它是否已经包括了一个 Git 仓库并不重要),一个交互式安装助手将引导您完成这个初始化操作。听起来有点炫,但实际上它只是在你的分支上配置了一些命名规则。 尽管如此,这个安装助手还是允许你使用自己喜欢的名字。我强烈建议你使用默认的命名机制,并且一步一步地确定下去。

分支的模式

git-flow 模式会预设两个主分支在仓库中:

  • master 只能用来包括产品代码。你不能直接工作在这个 master 分支上,而是在其他指定的,独立的特性分支中(这方面我们会马上谈到)。不直接提交改动到 master 分支上也是很多工作流程的一个共同的规则。
  • develop 是你进行任何新的开发的基础分支。当你开始一个新的功能分支时,它将是_开发_的基础。另外,该分支也汇集所有已经完成的功能,并等待被整合到 master 分支中。

master

这两个分支被称作为 长期分支。它们会存活在项目的整个生命周期中。而其他的分支,例如针对功能的分支,针对发行的分支,仅仅只是临时存在的。它们是根据需要来创建的,当它们完成了自己的任务之后就会被删除掉。

长期分支

让我们开始探索一些在现实应用中可能遇到的案例吧!

功能开发

对于一个开发人员来说,最平常的工作可能就是功能的开发。这就是为什么 git-flow 定义了很多对于功能开发的工作流程,从而来帮助你有组织地完成它。

开始新功能

让我们开始开发一个新功能 “rss-feed”:

1
2
3
4
5
6
$ git flow feature start rss-feed
Switched to a new branch 'feature/rss-feed'

Summary of actions:
- A new branch 'feature/rss-feed' was created, based on 'develop'
- You are now on branch 'feature/rss-feed'

正如上面这个新功能一样,git-flow 会创建一个名为 “feature/rss-feed” 的分支(这个 “feature/” 前缀 是一个可配置的选项设置)。你已经知道了,在你做新功能开发时使用一个独立的分支是版本控制中最重要的规则之一。 git-flow 也会直接签出这个新的分支,这样你就可以直接进行工作了。

完成一个功能

经过一段时间艰苦地工作和一系列的聪明提交,我们的新功能终于完成了:

1
2
3
4
5
6
7
8
$ git flow feature finish rss-feed
Switched to branch 'develop'
Updating 6bcf266..41748ad
Fast-forward
feed.xml | 0
1 file changed, 0 insertions(+), 0 deletions(-)
create mode 100644 feed.xml
Deleted branch feature/rss-feed (was 41748ad).

最重要的是,这个 “feature finish” 命令会把我们的工作整合到主 “develop” 分支中去。在这里它需要等待:

  • 一个在更广泛的 “开发” 背景下的全面测试。
  • 稍后和所有积攒在 “develop” 分支中的其它功能一起进行发布。 之后,git-flow 也会进行清理操作。它会删除这个当下已经完成的功能分支,并且换到 “develop” 分支。

管理 releases

Release 管理是版本控制处理中的另外一个非常重要的话题。让我们来看看如何利用 git-flow 创建和发布 release。

创建 release

当你认为现在在 “develop” 分支的代码已经是一个成熟的 release 版本时,这意味着:第一,它包括所有新的功能和必要的修复;第二,它已经被彻底的测试过了。如果上述两点都满足,那就是时候开始生成一个新的 release 了:

1
2
$ git flow release start 1.1.5
Switched to a new branch 'release/1.1.5'

请注意,release 分支是使用版本号命名的。这是一个明智的选择,这个命名方案还有一个很好的附带功能,那就是当我们完成了release 后,git-flow 会适当地_自动_去标记那些 release 提交。

有了一个 release 分支,再完成针对 release 版本号的最后准备工作(如果项目里的某些文件需要记录版本号),并且进行最后的编辑。

完成 release

现在是时候按下那个危险的红色按钮来完成我们的release了:

1
git flow release finish 1.1.5

这个命令会完成如下一系列的操作:

  • 首先,git-flow 会拉取远程仓库,以确保目前是最新的版本。
  • 然后,release 的内容会被合并到 “master” 和 “develop” 两个分支中去,这样不仅产品代码为最新的版本,而且新的功能分支也将基于最新代码。
  • 为便于识别和做历史参考,release 提交会被标记上这个 release 的名字(在我们的例子里是 “1.1.5”)。
  • 清理操作,版本分支会被删除,并且回到 “develop”。 从 Git 的角度来看,release 版本现在已经完成。依据你的设置,对 “master” 的提交可能已经触发了你所定义的部署流程,或者你可以通过手动部署,来让你的软件产品进入你的用户手中。

hotfix

很多时候,仅仅在几个小时或几天之后,当对 release 版本作做全面测试时,可能就会发现一些小错误。 在这种情况下,git-flow 提供一个特定的 “hotfix” 工作流程(因为在这里不管使用 “功能” 分支流程,还是 “release” 分支流程都是不恰当的)。

创建 Hotfixes

1
$ git flow hotfix start missing-link

这个命令会创建一个名为 “hotfix/missing-link” 的分支。因为这是对产品代码进行修复,所以这个 hotfix 分支是基于 “master” 分支。 这也是和 release 分支最明显的区别,release 分支都是基于 “develop” 分支的。因为你不应该在一个还不完全稳定的开发分支上对产品代码进行地修复。

就像 release 一样,修复这个错误当然也会直接影响到项目的版本号!

完成 Hotfixes

在把我们的修复提交到 hotfix 分支之后,就该去完成它了:

1
$ git flow hotfix finish missing-link

这个过程非常类似于发布一个 release 版本:

完成的改动会被合并到 “master” 中,同样也会合并到 “develop” 分支中,这样就可以确保这个错误不会再次出现在下一个 release 中。 这个 hotfix 程序将被标记起来以便于参考。 这个 hotfix 分支将被删除,然后切换到 “develop” 分支上去。 还是和产生 release 的流程一样,现在需要编译和部署你的产品(如果这些操作不是自动被触发的话)。

原文地址:https://www.git-tower.com/learn/git/ebook/cn/command-line/advanced-topics/git-flow

swift代码中时长会遇到codable的用法

但是不太理解其中的含义,也不太理解如何使用

于是google后发现了一篇比较好的文章,点这里

从代码中,或者实例中我们会遇到类似的使用方法

1
struct Landmark: Hashable, Codable

继承了Codable的话,就需要实现其中的协议方法

It’s pretty easy, just use String or Int raw values which are implicitly assigned.

看几个例子,引用了上面提到的文章中的例子

1
2
3
enum PostType: Int, Codable {
case image, blob
}

这样用之后

image的值为0

blob的值为1

1
2
3
enum PostType: String, Codable {
case image, blob
}

这样用之后

image的值为字符串 “image”

blob的值为字符串 “blob”

下面看下这个特殊的特性如何使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
enum PostType : Int, Codable {
case count = 4
}

struct Post : Codable {
var type : PostType
}

let jsonString = "{\"type\": 4}"

let jsonData = Data(jsonString.utf8)

do {
let decoded = try JSONDecoder().decode(Post.self, from: jsonData)
print("decoded:", decoded.type)
} catch {
print(error)
}

如果这个例子能够看懂的话,那么下下面这个例子也是很容易理解的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
struct Landmark: Hashable, Codable {
var id: Int
var name: String
fileprivate var imageName: String
fileprivate var coordinates: Coordinates

var state: String
var park: String
var categroy: Category

var locationCoordinate: CLLocationCoordinate2D {
CLLocationCoordinate2D(latitude: coordinates.latitude, longitude: coordinates.longitude)
}

enum Category: String, Hashable {
case featured = "Featured"
case lakes = "Lakes"
case rivers = "Rivers"
}
}

运行后,会遇到如下错误

报错信息如下

1
Type 'Landmark' does not conform to protocol 'Decodable'

完整的,无问题的代码如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
struct Landmark: Hashable, Codable {
var id: Int
var name: String
fileprivate var imageName: String
fileprivate var coordinates: Coordinates

var state: String
var park: String
var categroy: Category

var locationCoordinate: CLLocationCoordinate2D {
CLLocationCoordinate2D(latitude: coordinates.latitude, longitude: coordinates.longitude)
}

enum Category: String, Hashable, Codable {
case featured = "Featured"
case lakes = "Lakes"
case rivers = "Rivers"
}
}

从上面的例子中可以看出,Codable使用在struct和enum组合的使用方式中。

event - 错误事件和仅处理事件一次

当使用 eventEmitter.on() 注册监听器时,监听器会在每次触发命名事件时被调用。

1
2
3
4
5
6
7
8
9
10
const EventEmitter = require("events");

const emitter = new EventEmitter();

emitter.on("event", function () {
console.log("event emitter");
});

emitter.emit("event");
emitter.emit("event");

运行后得到的结果如下

1
2
3
$ node main.js
event emitter
event emitter

使用 eventEmitter.once() 可以注册最多可调用一次的监听器。 当事件被触发时,监听器会被注销,然后再调用。

1
2
3
4
5
6
7
8
9
10
const EventEmitter = require("events");

const emitter = new EventEmitter();

emitter.once("event", function () {
console.log("event emitter");
});

emitter.emit("event");
emitter.emit("event");

运行后得到的结果如下

1
2
$ node main.js
event emitter

(错误事件)Error events

1
2
3
4
5
const EventEmitter = require("events");

const emitter = new EventEmitter();

emitter.emit("error", new Error("i am a error"));

运行结果如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
$ node main.js
events.js:174
throw er; // Unhandled 'error' event
^

Error: i am a error
at Object.<anonymous> (/Users/durban/nodejs/main.js:45:23)
at Module._compile (internal/modules/cjs/loader.js:778:30)
at Object.Module._extensions..js (internal/modules/cjs/loader.js:789:10)
at Module.load (internal/modules/cjs/loader.js:653:32)
at tryModuleLoad (internal/modules/cjs/loader.js:593:12)
at Function.Module._load (internal/modules/cjs/loader.js:585:3)
at Function.Module.runMain (internal/modules/cjs/loader.js:831:12)
at startup (internal/bootstrap/node.js:283:19)
at bootstrapNodeJSCore (internal/bootstrap/node.js:623:3)
Emitted 'error' event at:
at Object.<anonymous> (/Users/durban/nodejs/main.js:45:9)
at Module._compile (internal/modules/cjs/loader.js:778:30)
[... lines matching original stack trace ...]
at bootstrapNodeJSCore (internal/bootstrap/node.js:623:3)

再看下面实例

1
2
3
4
5
6
7
8
9
10
const EventEmitter = require("events");

const emitter = new EventEmitter();

emitter.on("error", function (error) {
console.log("i am catch a error");
console.log(error);
});

emitter.emit("error", new Error("i am a error"));

运行后结果如下

1
2
3
4
5
6
7
8
9
10
11
12
$ node main.js
i am catch a error
Error: i am a error
at Object.<anonymous> (/Users/durban/nodejs/main.js:77:23)
at Module._compile (internal/modules/cjs/loader.js:778:30)
at Object.Module._extensions..js (internal/modules/cjs/loader.js:789:10)
at Module.load (internal/modules/cjs/loader.js:653:32)
at tryModuleLoad (internal/modules/cjs/loader.js:593:12)
at Function.Module._load (internal/modules/cjs/loader.js:585:3)
at Function.Module.runMain (internal/modules/cjs/loader.js:831:12)
at startup (internal/bootstrap/node.js:283:19)
at bootstrapNodeJSCore (internal/bootstrap/node.js:623:3)

event(事件)的简单使用记录再次

Party popper The Elephpant is going on further adventures!

PHP 8 is in feature freeze, and beta1 is released. Get on with yer testing! Bugs to https://bugs.php.net

There are also new security releases stable PHP versions: 7.2.33, 7.3.21 and 7.4.9. Get upgrading!

这里是php的todo列表 https://wiki.php.net/todo/php80

Security Release: Laravel 6.18.34, 7.23.2

By Taylor Otwell
Time Aug, 6 2020

在Laravel的早期版本中,可以批量分配包含模型表名的Eloquent属性:

1
$model->fill(['users.name' => 'Taylor']);

这样做时,Eloquent会为您从属性中删除表名。这是Eloquent的“便利”功能,没有记录。

但是,与验证配对时,这可能导致意外的和未经验证的值被保存到数据库中。因此,我们从批量分配操作中删除了对表名的自动剥离,以使属性通过典型的“可填充” /“受保护”逻辑。包含未明确声明为可填充的表名的任何属性都将被丢弃。

对于在批量分配期间依赖未记录的表名称剥离的应用程序,此安全版本将是一项重大更改。

原文地址:
https://blog.laravel.com/security-release-laravel-61834-7232

private和fileprivate的区别,private在类中代表是私有的,只能本类使用

但是在swift也有,除此之外还有一个fileprivate

其实这两种访问控制形式相似,但是有两个区别。
如果标记了文件专用文件,则可以在声明的文件中的任何位置读取该文件(即使在类型之外)。
另一方面,私有属性只能在声明它的类型内部或在同一文件中创建的该类型的扩展内部读取。
在实践中,您可能会发现private的使用明显多于fileprivate。

简单通过实例看下private和fileprivate的区别

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import Cocoa

class A {
private func foo() {}
fileprivate func bar() {}

func baz() {
foo()
bar()
}
}

extension A {
func test() {
foo()
bar()
}
}

let a = A()
a.foo() // 'foo' is inaccessible due to 'private' protection level
a.bar()
a.test()
a.baz()

现看个简单的关于事件使用的代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
const Emitter = require("events");

const emitter = new Emitter();

emitter.on("event_1", function (name, age) {
setImmediate(function () {
console.log("event_1 immediate");
});

console.log(`event_1 ${name} age is ${age}`);
});

emitter.on("event_2", function (name, age) {
setImmediate(function () {
console.log("event_2 immediate");
});
console.log(`event_2 ${name} age is ${age}`);
});

emitter.on("event_3", function (name, age) {
process.nextTick(function () {
console.log("event_3 nextTick");
});

console.log(`event_3 ${name} age is ${age}`);
});

emitter.emit("event_1", "xiaowang", 20);
emitter.emit("event_2", "xiaoli", 30);
emitter.emit("event_1", "xiaowang2", 20);
emitter.emit("event_2", "xiaoli2", 30);
emitter.emit("event_3", "xiaowei", 29);
emitter.emit("event_3", "xiaowei2", 29);

输出的结果如下

1
2
3
4
5
6
7
8
9
10
11
12
event_1 xiaowang age is 20
event_2 xiaoli age is 30
event_1 xiaowang2 age is 20
event_2 xiaoli2 age is 30
event_3 xiaowei age is 29
event_3 xiaowei2 age is 29
event_3 nextTick
event_3 nextTick
event_1 immediate
event_2 immediate
event_1 immediate
event_2 immediate

从上面的输出结果可以看出,emit触发事件的时候,事件的执行顺序是按照顺序从上到下依次执行(应该可以看作是事件的同步)

1
2
3
4
5
6
event_1 xiaowang age is 20
event_2 xiaoli age is 30
event_1 xiaowang2 age is 20
event_2 xiaoli2 age is 30
event_3 xiaowei age is 29
event_3 xiaowei2 age is 29

再看下这里的输出结果,会发现,无论执行多少次这个顺序是不会变的(应该可以看作是事件的异步)

1
2
3
4
5
6
event_3 nextTick
event_3 nextTick
event_1 immediate
event_2 immediate
event_1 immediate
event_2 immediate

process.nextTick和setImmediate,作为异步处理的函数,居然process.nextTick会比setImmediate的调用更先一步执行

具体的原因可以参考官方的文章《Node.js 事件循环,定时器和 process.nextTick()》

从官方的文章中也得出结果,setImmediate应该多用,process.nextTick应该少用,虽然这需要一个漫长的过程

大多数 Node.js 核心 API 构建于惯用的异步事件驱动架构,其中某些类型的对象(又称触发器,Emitter)会触发命名事件来调用函数(又称监听器,Listener)。

先看下event(事件)如何使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
const Emitter = require("events");

const emitter = new Emitter();

emitter.on("event", function () {
console.log("an event occur");
});

emitter.on("event1", function () {
console.log("an event1 occur");
});

emitter.emit("event");
emitter.emit("event1");

上面我们绑定了两个事件分别为eventevent1,然后通过emit分别触发了这两个事件

运行结果如下

1
2
an event occur
an event1 occur

事件触发了,那么如何能在触发事件的时候传递参数,看下面的代码

1
2
3
4
5
6
7
8
9
10
11
12
const Emitter = require("events");

const emitter = new Emitter();

emitter.on("run", function (name, distance) {
console.log(`【${name}】跑了${distance}米`);

console.log(this);
console.log(this === emitter);
});

emitter.emit("run", "xiaowang", 20);

输出结果如下

1
2
3
4
5
6
【xiaowang】跑了20米
EventEmitter {
_events: [Object: null prototype] { run: [Function] },
_eventsCount: 1,
_maxListeners: undefined }
true

在这里不建议使用 ES6 Arrow Functions

1
2
3
4
5
6
7
8
9
10
11
12
const Emitter = require("events");

const emitter = new Emitter();

emitter.on("run", (name, distance) => {
console.log(`【${name}】跑了${distance}米`);

console.log(this);
console.log(this === emitter);
});

emitter.emit("run", "xiaowang", 20);

运行结果如下

1
2
3
【xiaowang】跑了20米
{}
false
0%