使用NetlifyCMS在线编辑Github上的博客

Netlify CMS 介绍

使用Netlify CMS我感觉有以下优点:

  • 无缝支持Hexo 等十几种主流静态网站生成器 的 文章后台管理*
  • 可视化在线编辑、新增github 上的markdown
  • 自带图片上传功能
  • 自动部署

支持列表:
Jekyll, GitBook,Hugo, Gatsby, Nuxt, Next, Gridsome, Zola,Hexo, Middleman, Jigsaw,Spike ,Wyam,Pelican,VuePress,Elmstatic,11ty,preact-cli

为什么使用它

对于我的情况:使用Hexo 网站生成器,托管在github上 https://github.com/Troy-Yang/troy-yang.github.io,其中Source branch是存放markdown等生成前分支,Master branch存放的是生成后的静态文件分支。
对于以前,如果要写一篇文章,基本是在source 分支里,新增一个markdown文件(可github上在线添加或者本地新增然后push),然后自动触发github 上配置的travis 自动部署流程,整体感觉已经很不错了。现在配置上Netlify CMS后, 可视化的在线编辑以及图片管理更加方便,可以随时随地发文章。
可惜, Netlify有个致命缺点:需要翻墙访问

HEXO NetlifyCMS 配置

只需要在hexo 的source/ 目录下添加admin 目录,新增下面两个文件:

1
2
config.yml
index.html

config.yml需要根据自己情况进行配置:

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
backend:
name: git-gateway
branch: Source

# This line should *not* be indented
media_folder: "source/images/uploads" # Media files will be stored in the repo under images/uploads
public_folder: "/images/uploads" # The src attribute for uploaded media will begin with /images/uploads

collections:
- name: "blog" # Used in routes, e.g., /admin/collections/blog
label: "Post" # Used in the UI
folder: "source/_posts" # The path to the folder where the documents are stored
create: true # Allow users to create new documents in this collection
slug: "{{slug}}" # Filename template, e.g., YYYY-MM-DD-title.md
fields: # The fields for each document, usually in front matter
- {label: "Layout", name: "layout", widget: "hidden", default: "post"}
- {label: "Title", name: "title", widget: "string"}
- {label: "Publish Date", name: "date", widget: "datetime"}
- {label: "Tags", name: "tags", widget: "list", required: false}
- {label: "Categories", name: "categories", widget: "list", required: false}
- {label: "Photos", name: "photos", widget: "list", required: false}
- {label: "Excerpt", name: "excerpt", widget: "string", required: false}
- {label: "Body", name: "body", widget: "markdown"}
- {label: "Permalink", name: "permalink", widget: "string", required: false}
- {label: "Comments", name: "comments", widget: "boolean", default: true, required: false}

index.html

1
2
3
4
5
6
7
8
9
10
11
12
13
<!doctype html>
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Content Manager</title>
</head>
<body>
<!-- Include the script that builds the page and powers Netlify CMS -->
<script src="https://unpkg.com/netlify-cms@^2.0.0/dist/netlify-cms.js"></script>
<script src="https://identity.netlify.com/v1/netlify-identity-widget.js"></script>
</body>
</html>

Netlify 端配置

创建 Netlify website

注意:和大部分人的做法不同的是,我Deploy到的地方并不是托管在Netlify自己的平台上,而是github上,所以这里我选择部署的是Source分支,而不是Master,因为我只是想要Netlify去修改我的Source分支,然后触发Travis自动发布到Master分支。

但是我依旧需要填写Netlify的部署,因为Netlify会自动帮我创建域名为troyyang.netlify.com的网站,任何我Source分支上的修改也会触发这个网站的自动部署

开启Netlify Identity 和 Git Gateway

在Setting 的 Identity选项下:

  1. Enable Identity service
  2. External providers 新增github
  3. Enable Git Gateway

发布测试

打开 https://troyyang.netlify.com/admin/ 然后使用github Oauth登录即可看到:

新增文章后, 你会发现github 上的source目录下的_post 目录的markdown 文件新增了,如果上传了图片,也会看到source目录下多了images/upload目录,同时https://troyyang.netlify.comhttps://troyyang.com 下也自动发布了新的文章, 两者都是因为Source分支里新增了文件导致的自动部署。



问题

当我尝试打开 https://troyyang.com/admin/ 使用github Oauth登录时,结果报错,而https://troyyang.netlify.com/admin/则没有:

1
Failed to load settings from /.netlify/identity

我怀疑是因为https://troyyang.com是托管在github上,而不是netlify上导致的。

Enjoy!

Top

AWS系列之使用无服务器架构你的网站


Serverless 有什么用啊?

Jason最近又出新想法了,想要做一个简单的用户管理系统,好的,没问题,不就是在服务器上安装数据库,部署好网站吗?可答案是no,他不是专业人员,我也不可能永远维护这个服务器,更重要的是服务器开着就要美刀啊,还不能停,怎么办?有没有可以不用服务器的网站,有啊,你自己的静态博客不就是只用到了s3或者github的静态页面托管吗?可是数据库呢,后台api呢?额,这个嘛。。。

好了,成功引出话题,要知道这是21世纪的云时代,只有你想不到,没有做不到的,这不,AWS早就提出了Serverless解决方案:S3 + GateWay API + Lambda + DynamDB,其中举例的一个天气的app架构:

image

其中s3做静态页面托管,用户触发点击事件,调用Gateway API提供到接口,接口映射到Lambda服务端接口,Lambda再负责去处理和数据库相关到操作。整个过程不需要服务器,而且费用是极低的,按量付费,可扩展性也很强,基本做到可配置化。说了这么多,还是得用过才知道好不好。

实现思路

  1. 服务端RestFull: Node express 实现RestFull API
  2. 创建lambda并上传服务端代码
  3. 配置API Gateway映射到lambda函数
  4. 客户端实现: Bootstrap 实现登录 和 管理页面
  5. 修改客户端api接口地址并上传至S3

其中,到第三步的时候我们就已经创建好了一个完整的无服务器的 Restfull API,剩下的就是客户端调用了,客户端调用这个就可以是五花八门的了,这也不是本篇文章的重点。

简单 RestFull 服务端实现

服务端的实现和平时实现一个Node RestFull api的完全没有任何区别, 部分代码如下:
app.js

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
'use strict';

const express = require('express');
const app = express();
const bodyParser = require('body-parser');
const cors = require('cors');

app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));
app.use(cors());

let contacts = require('./data');

app.get('/api/contacts', (request, response) => {
if (!contacts) {
response.status(404).json({ message: 'No contacts found.' });
}
response.json(contacts);
});

const hostname = 'localhost';
const port = 3001;
const server = app.listen(port, hostname, () => {
console.log(`Server running at http://${hostname}:${port}/`);
});

module.exports = app

这一步做完,确保所有接口都能通过访问 localhost:3001/api/contacts

image

aws-serverless-express

要使得上面的服务端代码能在lambda中允许,只需借助 npm 包aws-serverless-express

在目录下新增 lambda.js文件

1
2
3
4
5
6
7
// lambda.js
'use strict'
const awsServerlessExpress = require('aws-serverless-express')
const app = require('./app')
const server = awsServerlessExpress.createServer(app)

exports.handler = (event, context) => awsServerlessExpress.proxy(server, event, context)

这也是为什么我们要在 app.js最后一行exports的原因

1
module.exports = app

此时,将所有文件包括node_module目录全部打包为.zip 文件为后面使用。

创建 lambda

创建 IAM role

创建 Lambda的IAM Role是必须的,他指定了当前lamda能访问到的资源有那些,从我们的列子中,我们需要用到DynamoDB, 同时为了方便debug,我们还需要用到cloudwatch服务 (这个对于查找问题非常有用)。

登录aws console,打开 Service 找到 IAM ,再选择Roles,点击 create role 按钮 后如图,(第三步可选):
image
image
image

创建 lambda 函数

打开Service 找到lambda, 选择 create function:
image

创建后,在代码输入种类中选择上传 .zip 文件:
image

将服务端代码整个打包 (注意一定要包括packages目录下的所有文件)然后上传,大小不能超过10m,如果超过了,可以在代码输入种类选择s3上传。上传完成后,指定入口文件(即在处理程序)为 lambda.handler, 此文件将会映射到 lambda.js文件,一般情况,如果上传的zip包不是很大,aws会自动列出zip项目目录可供在线编辑,但如果大了的化,比如好几兆,则有可能不会列出项目目录,每次修改又只能重新上传。

image

当然,如果node 代码里包括了一些环境变量,你也可以为 lambda 做一些环境变量的设置:

image

一切ok后,就可以测试了,关于lambda的测试,则相对还比较麻烦,我也是最近才稍微懂那么一点。

测试 lambda 函数

在创建好的lambda 函数旁,点击配置测试事件按钮,在弹出对话框创建测试事件中选择创建新测试事件,在事件模板中选择 Amazon API Gateway AWS Proxy, 并给个测试名称,如图:
image

选择Amazon API Gateway AWS Proxy是因为我们的这个lambda函数最终会被API Gateway 触发调用,同时由于默认的事件模板是 post 的请求方式,而我们的这个服务端只有一个api/contacts的get方法,所以我们需要更改事件内容为:

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
{
"resource": "/{proxy+}",
"path": "/api/contacts",
"httpMethod": "get",
"headers": {
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8",
"Accept-Encoding": "gzip, deflate, sdch",
"Accept-Language": "en-US,en;q=0.8",
"Cache-Control": "max-age=0",
"CloudFront-Forwarded-Proto": "https",
"CloudFront-Is-Desktop-Viewer": "true",
"CloudFront-Is-Mobile-Viewer": "false",
"CloudFront-Is-SmartTV-Viewer": "false",
"CloudFront-Is-Tablet-Viewer": "false",
"CloudFront-Viewer-Country": "US",
"Host": "1234567890.execute-api.ap-northeast-1.amazonaws.com",
"Upgrade-Insecure-Requests": "1",
"User-Agent": "Custom User Agent String",
"Via": "1.1 08f323deadbeefa7af34d5feb414ce27.cloudfront.net (CloudFront)",
"X-Amz-Cf-Id": "cDehVQoZnx43VYQb9j2-nvCh-9z396Uhbp027Y2JvkCPNLmGJHqlaA==",
"X-Forwarded-For": "127.0.0.1, 127.0.0.2",
"X-Forwarded-Port": "443",
"X-Forwarded-Proto": "https"
}
}

保存测试事件,并点击执行,如果一切正常,会得到如下:
image

创建 API Gateway

找到Service下到API Gateway,并点击新建 api,

image
新增 api 资源(路径)
image
选择 api 资源,再新增子资源,并选为proxy
image
选择 proxy 资源,创建 集成环境为我们创建好的lambda 函数
image

创建完成之后,在操作选项中,选择部署,弹出对话框并命名为dev阶段:
image

部署完成后,得到如下结果:
image

API Gateway 测试

在部署完成后,我们会在上述结果中得到发布出来的api 地址为

https://ijihnuupmh.execute-api.ap-northeast-1.amazonaws.com/dev

此时如果直接访问,会得到Missing Authentication Token的错误,原因是我们地址不对

1
{"message":"Missing Authentication Token"}

正确地址应该为:

https://ijihnuupmh.execute-api.ap-northeast-1.amazonaws.com/dev/api/contacts

image

由于上述地址是永久的,除非你重新部署,所以我们可以放心的使用用来作为api地址。还有一个就是API Gateway似乎是不收取费用的,只会按照lambda函数的调用次数来收取费用,好像每月前100万次请求是免费的。所以还是相当划算。

OK! 一个无服务器的后端 api 就这样搭建好了,剩下的就是前端静态资源的托管了

前端静态资源

直接上传html,js,css 等静态资源到S3就好了,具体可以参见另一篇博客 AWS系列之S3 + Cloudfront搭建https静态网站

Top

AWS系列之S3 + Cloudfront搭建https静态网站


本文和之前写的《正确使用AWS S3的方式之打造自己的https图床》 内容非常像,但也有新的内容如自动上传部署和自定义证书、Route53部分,这里主要补充新的内容。

温馨提醒

  1. 个人用户请注册AWS 全球账号,因为AWS 中国账号似乎只对企业开发而无法注册。
  2. AWS S3默认地址中国无法访问,需要使用Cloudfront的新地址才能访问,而且访问速度不慢,你能感觉到我博客的图片加载慢吗?
  3. S3、Cloudfront、Route53等收费异常的低,几乎可以忽略。

架构概述

image
采用Hexo作为静态网站生成器,主题使用的正是我自己开发的hexo-theme-twentyfifteen-wordpress,整个网站代码托管在github的私人repo下。当写好文章后,使用Hexo生成静态代码html+css+js+image,并使用写好的s3 SDK 上传工具到指定aws 的存储桶里进行静态托管 (之前使用的是github的Travis 自动部署,但由于私人Repo需要收费,于是放弃Travis改用自己的工具上传)。上传到S3里之后,使用Cloudfront做内容分发,并绑定自定义的https证书,最后,使用Route53做自定义域名的绑定。

生成 IAM access key 用户子账号

此账号可用于编程的方式访问AWS 的所有指定资源,这里我们创建的IAM 账号只需要有S3的读写权限

进入 https://console.aws.amazon.com/iam/home?region=ap-northeast-1#/users

选择add user后, 一定要选择programmatic access这种编程方式的子账号,而另一个console账号针对的是用户名,密码登录的子账号
image

指定该账号可访问的权限
image

保存access id和key
image

使用S3 SDK自动上传

默认情况下,所有Hexo编译后的文件都放在public文件件下,所以只需要拷贝到S3 存储桶下,当然可以手动拷贝,但是实在太麻烦。所以写了个node tool去自动上传(部署)。

下载
–s3-deploy
——config.json
——index.js

config.js 里包括的是AWS AMI 账号信息,确保region使正确的区域, 具体参考区域列表

1
2
3
4
5
{
"accessKeyId": "xxxxxx",
"secretAccessKey": "xxxxx",
"region": "ap-northeast-1"
}

index.js,其中myenglishtutor.eu是存储桶的名字(命名的时候请用域名)

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
var path = require("path");
var fs = require('fs');
var mime = require('mime');
var AWS = require('aws-sdk');
AWS.config.loadFromPath('./s3-deploy/config.json');
let s3 = new AWS.S3();

const uploadDir = function (s3Path, bucketName) {
function walkSync(currentDirPath, callback) {
fs.readdirSync(currentDirPath).forEach(function (name) {
var filePath = path.join(currentDirPath, name);
var stat = fs.statSync(filePath);
if (stat.isFile()) {
callback(filePath, stat);
} else if (stat.isDirectory()) {
walkSync(filePath, callback);
}
});
}

walkSync(s3Path, function (filePath, stat) {
let bucketPath = filePath.substring(s3Path.length + 1);
let mimeType = mime.getType(bucketPath);
let params = {
Bucket: bucketName,
Key: bucketPath.replace(/\\/g, '/'),
Body: fs.readFileSync(filePath),
ContentType: mimeType
};
s3.putObject(params, function (err, data) {
if (err) {
console.log(err)
} else {
console.log('Successfully uploaded ' + bucketPath + ' to ' + bucketName);
}
});
});
};

uploadDir("public", "myenglishtutor.eu");

最后在package.json文件中,添加运行脚本:

1
2
3
4
"scripts": {
"start": "hexo clear & hexo g & hexo server",
"deploy": "hexo clear & hexo g & node ./s3-deploy/index"
},

使用Travis 自动部署

如果代码是托管到github上或者支持Travis的服务,可以是用下面的.travis.yml配置达到CI, CD,请在travis.org中配置好环境变量$AWS_ACCESS_ID, AWS_SECRET_KEY,AWS_REGION

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
language: node_js
node_js: stable

script: true

# S: Build Lifecycle
install:
- npm install

before_install:
- git submodule update --init --remote --recursive

#before_script:
# - npm install -g gulp

script:
- hexo g
# E: Build LifeCycle


before_deploy:
# - zip -r latest *
# - mkdir -p dpl_cd_upload
# - mv latest.zip dpl_cd_upload/latest.zip
- cp -a source/.well-known public/

deploy:
- provider: s3
access_key_id: ${AWS_ACCESS_ID}
secret_access_key: ${AWS_SECRET_KEY}
local_dir: public
skip_cleanup: true
on:
repo: Troy-Yang/troy-yang.github.io
branch: source
bucket: troyyang.com
region: ${AWS_REGION}

https自定义证书

想要支持https,那么证书是必须的,可以直接开启cloudfront,默认就会添加cloufront生成证书,如
image

如果想要自定义证书,则需要自己在ACM(AWS Certificate Manager)申请证书,并做txt域名验证,一切ok后则会得到:
image

申请步骤如下:
进入 https://console.aws.amazon.com/acm/home?region=us-east-1#/wizard/
填写域名:
image
选择验证方式:(DNS验证方便,Email没试过,好像很麻烦)
image
拷贝name和value值,保存并在DNS服务器中添加CNAME记录,如果DNS是使用AWS自家的Route53,则非常方便,只需要在相应的Domain下,添加 Record Set 记录, 类型选择CNAME。如果是在万网或者Cloudflare,也是非常方便的。
image
记录添加好后,等待验证通过后(大概几至十几个小时后),状态从pending 变为 issued,就说明证书通过,该域名已合法。
image

Cloudfront 中使用自定义证书

证书有了之后,只需要将其添加到创建的Cloudfront中就可以了
image

请注意选择Custom SSL Certificate, 然后输入框中,AWS会自动列出可用的证书列表,如果没有,则点击Request or Import a certificate with ACM 选择上面新增的就好了
在浏览器访问这个cloudfront地址,就可以看到https的标志,查看这个https证书就可以得到自定义的这个域名,而不是cloudfront开头的,看起来是不是很高大上。

绑定Cloudfront 到自定义DNS

上面的cloudfront地址如果用于提供API之类的接口地址倒是无所谓, 但是如果是别人访问的地址,那肯定不行的,还需要添加一条A记录,将自己的域名和上述地址进行绑定。同理,如果是在Route53,只需要添加一条A记录就好了,大致如图:
image

保存后,过几分钟就可以通过自己域名,访问到S3中的内容,并且证书显示的是自己域名。
image

Top

AWS系列之 myenglishtutor基于AWS生态的广泛使用


写在开头

看着系统生成的写作时间,2018年5月12日,恐怕是让所有四川人都难以忘怀的日子,在此缅怀十年前大地震遇难的同胞,也希望曾遭受苦痛的同胞十年后的今天一切安好!

回想在给Jason兼职工作的这半年多,一个人把 image https://myenglishtutor.eu 从0到1的把网站一点一点建好,感觉像个自己的孩子,总想要给他最好的,但也由于个人精力有限,很多想做的都没去做。尤其是AWS的生态让我印象深刻,所以想要写下这一个系列的技术感悟。

AWS系列之S3 + Cloudfront搭建https hexo静态网站

使用hexo做静态内容生成,s3托管静态网站内容,使用cloudfront做内容分发及https证书自动生成,route53做域名DNS解析,还有部分工具如依托s3 SDK做代码自动上传部署,google driver 自动同步等。
hexo、s3、cloudfront、route53、certification、aws s3 SDK auto sync file and deployment

AWS系列之 Stripe 国际支付

服务端使用Lambda、API Gateway实现的无服务器服务,客户端使用Stripe的Element.js类库
Lambda、API Gateway

AWS系列之结合OpenTek实现多人实时Web视频通话、教学

使用s3做视频前端和后台管理的代码托管, Lambda+DynamoDB+API Gateway实现Serverless搭建的node express无服务器服务端。
s3、openTek SDK、Lambda、DynamoDB、API Gateway、Angular 1.0、bootstrap

AWS系列之Polly服务实现AI文本到语音翻译

使用aws的Polly服务实现文本转语音的翻译,服务端搭建的Serverless服务端,客户端使用自己写的hexo plugin功能调用API。
Lambda、API Gateway、SNS、s3、Hexo plugin

AWS系列之使用Wowza streaming实现视频直播+弹幕服务

架构上使用EC2负载均衡和自动扩容实现可伸缩视频直播服务。弹幕使用web socket实现双向通信,客户端采用对手机H5播放支持。
s3、EC2、Load banance、ASG、cloudfront、linux、 danmaku、h5 video

这里也打个广告简单介绍一下Jason和他的myenglishtutor,从名字也大概知道他是个英语老师和他的个人网站,Jason是个地道的英国人,浓厚的英国口音以及超过十几年英语专业教学经验,作为myenglishtutor的开发者,我为他集成了包括视频直播,1对1,1对多的视频教学直播,支付宝支付,词法解释等功能,所以不用担心教学方式及支付等问题。如果有意专业英语要求的个人或者团体,欢迎直接联系我,有更低的折扣等你。

Top

正确使用AWS S3的方式之打造自己的https图床


写过博客的人都知道图床,一个托管自己博客图片的地方,当然托管到自己的服务器另当别论。常见的图床可以是新浪博客,七牛云,imgur等,但是都是有各种问题,比如我之前使用的是七牛云(也曾在《给Github自定义域名加上HTTPS》博文上推荐使用),用起来相当不错,只可惜后来备案信息过期了,导致无法再使用自定义域名,更可悲的是,https不再支持,意味着尽管我的博客是https但由于有内容是http的,只能被浏览器认为是mixed-content的。

但是,前几天无意发现一片新大陆,使用aws s3结合cloudfront distribution 可以借助亚马逊云无缝快速托管自己的图片还自带https,而费用几乎是很小的,按量收费。

步骤概述

(如果不需要有自定义图片的域名,第三步可选)

  1. 创建一个图片s3 bucket并公开。
  2. 创建cloudfront distribution并绑定S3 bucket和默认证书以支持https
  3. 在DNS服务商(我的是cloudflare)创建图床域名,并绑定cloudfront域名地址

全球亚马逊 Or 亚马逊中国?

两者区别好像挺大的,后者曾经注册过,但是不知为什么没通过审核,可能需要公司邮箱吧。并且,如果考虑到备案等因素,建议使用全球亚马逊。(需要绑定VISA信用卡)
全球亚马逊地址是:https://console.aws.amazon.com/console/home

创建S3 Bucket(存储桶)

账号创建成功后,进入S3控制台https://s3.console.aws.amazon.com,存储桶名称以待托管域名命名,比如我的是 images.troyyang.com,其他项首先都选择默认,待会再一项一项改。
image

访问权限设置

在存储桶的权限页面,选择存储桶策略,键入下面的值:

1
2
3
4
5
6
7
8
9
10
11
12
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "PublicReadForGetBucketObjects",
"Effect": "Allow",
"Principal": "*",
"Action": "s3:GetObject",
"Resource": "arn:aws:s3:::images.troyyang.com/*"
}
]
}

image

静态托管

存储桶创建成功后,进入属性页面,选择静态网站托管,键入索引文件index.html,错误文档error.html,然后保存。此时,公共访问页面已经生成,终端节点如下:
http://images.troyyang.com.s3-website-ap-northeast-1.amazonaws.com

image
上传自己的所有图片在此存储桶下,然后加上文件后缀就应该可以访问了,然而现实是残酷的,在我大天朝下,这个地址有时候是无法访问当的 。。。WTF。。。于是,得进行下面的自定义域名步骤

CloudFront Distribution

上面地址是AWS自动生成的访问域名,并且只支持http,想要支持https,并且绑定自定义域名(images.troyyang.com),需要使用到CloudFront Distribution。

CloudFront Distribution 是AWS的内容分发(CDN)使得全球各地都能以最快的速度访问到AWS最近的节点(对于中国,最近的是东京,经测,也已经足够快),并且可绑定或者生产SSL证书。

创建 Distribution

打开 https://console.aws.amazon.com/cloudfront/home, 选择Create Distribution, 传输方式选择Web选项 Get Started,在很多选项中,主要注意几项就好了(都是可后期修改):

  • Origin Domain Name中选择刚才所建的S3 Bucket 域名
  • Alternate Domain Names(CNAMEs)填写自定义域名(没有的话,可不管), 这里是 images.troyyang.com
  • SSL Certificate 暂时选默认Default CloudFront Certificate (*.cloudfront.net)
  • Price Class 可以只选择Use Only US, Canada, Europe and Asia

image

一切配置好后,静静等待几个小时就会看到Distribution部署成功,大致结果如下:

image

此时,得到Distribution 的新访问地址 d2dxo9yo9kwqp2.cloudfront.net,这个时候,我们找一张在S3中存在的图片,加上https再次访问 https://d2dxo9yo9kwqp2.cloudfront.net/2017-5-21-https.png 一切OK

自定义证书(可选)

上面的证书是亚马逊自己提供,如果想要使用绑定自己的域名证书,可以使用AWS的Certificate Manager 证书服务,在自己的DNS服务商比如万网或者阿里云那里配置好验证方式,具体操作方法参考 https://docs.aws.amazon.com/zh_cn/acm/latest/userguide/gs-acm-request.html 。因为我暂时觉得没必要,所以没使用上。

绑定自定义域名(可选)

上面的是cloudfront分发的一个地址,虽然地址是固定的,但毕竟不是自家的域名,感觉不高大上,所以需要绑定上自己的图片域名。

由于我的DNS服务解析改为了Cloudflare,所以是以Cloudflare的来配置的域名,但和万网或者阿里云的配置完全一致,在DNS解析项中添加一条CNAME记录,指向Cloudfront分配的域名即可

image
等待绑定解析成功后,访问 https://images.troyyang.com/2017-5-21-https.png ,一切OK

Top

Stripe开发使用指南--国际支付(含支付宝)


前段时间,因为Jason让我帮忙把Stripe支付集成到他个人网站上去,让我有机会接触到支付系统开发,同时也因为苦于没有找到太多中文方面相关文档介绍,所以做个总结,也方便以后有需要的同学。
(更新) 2021.5 发现好些同学也在咨询如何集成微信支付,其实也是非常简单,所以新增了最后微信的实现,见最后
(更新) 2022.8 Source的方式已经不被推荐,推荐使用PaymentIntent

关于Stripe支付

第一次听说Stripe还是在几个月前的一个新闻上了解到,大致说的是美国总统都在使用它,极有可能成功下一个Paypal。这么受欢迎的一个支付平台到底有什么好处呢?我粗略搜集了一下:

  • 一条代码让你网站支持繁琐的国际支付功能。(对于创业公司,再合适不过)
  • 向全球化业务拓展会成为Stripe的机会。即使支付货币不同、方法不同,Stripe都能打通各自的渠道,让全球化交易不受支付阻碍。
  • 市值超过90亿美元,和Tweeter,Lyft,Best Buy等以及国内的 Alipay, WeChat等有合作

重点说下第二点,什么意思呢,就是说客户可以使用人民币支付,如果商家(收款方)是美国的银行的话,就自动转成美元,是英国的银行就自动转为英镑!(可惜暂时不支持商家是中国(但Stripe也可提供解决方案,就是使用Atlas去创建一个美国的代理公司)

而对于我们程序员的话,当然最关心第一条,因为他的宗旨就是开发极简,对开发人员超级友好!至于多友好呢,请往下看。

最简洁支付

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Stripe Checkout</title>
</head>
<body>
<form action="/your-server-side-code" method="POST">
<script
src="https://checkout.stripe.com/checkout.js" class="stripe-button"
data-key="[Publishable key]"
data-amount="999"
data-name="troy yang"
data-description="Widget"
data-image="https://stripe.com/img/documentation/checkout/marketplace.png"
data-locale="auto"
data-zip-code="true"
data-currency="eur">
</script>
</form>
</body>
</html>

就这么几行代码,我们就已经实现了客户端所有事:

image

真的是超级简单,但是这种方式是基于信用卡支付的界面,已经可以满足一半的支付方式,对于其他的三方支付,比如3D secure, 支付宝,微信,甚至比特币,Stripe为我们提供了其他方式,等下我就使用支付宝来举例。

注册 Stripe 账号

和注册支付宝账号一个道理,首先注册账号,然后绑定自己银行卡,BUT, 就像前面提到的,不支持中国,所以就算注册成功,也没法激活,也就没法收款。

对于中国商家怎么办呢,我能想到的就只有这几个办法:

  • 自己去支持国家去办理张银行卡
  • 使用国外的朋友银行卡
  • 使用Atlas

对于Jason来说,因为他是英国人,所以他可以创建他的主账号,然后添加我的stripe账号到他team memeber账号列表中,这样我就可以访问他账户下所有开发者需要的权限。邀请成功后,Dashboard页面

两个阶段

Stripe有两种模式,一个是测试模式(Test Mode),一个是生产模式(Live Mode),测试模式下产生的金钱交易都只用于测试,当所有测试通过后即可切换为Live模式。唯一的不同就是Publishable keySecret key, 一会我们会用到这两个值。
image

交易流程

Stripe有几个概念用于整个交易阶段和状态:
image

创建 Source

使用自己的Publishable key来创建一种source(比如Cards, 3D Secure, 支付宝,甚至比特币等), 创建source完了后,就会得到一个用于交易的Token或者是一个跳转到其他支持的三方支付平台(比如支付宝支付)页面等待用户支付。当用户支付(或者取消支付)完成,自动跳转回到指定结果页面。用户支付页面结束后,可能会得到三个状态:

  • source.chargeable 用户授权(支付)成功
  • source.failed 用户拒绝授权(支付)
  • source.canceled 超时支付

创建 Charge

当用户支付成功后,此时在Stripe端的支付状态变为source.chargeable,意思就是授权成功了,你可以在我支付宝平台上扣钱啦,所以,此时我们还需要使用Secret key来创建Charge来完成,官方推荐的是使用webhooks来捕捉状态,并且完成Charge的创建。当Charge完成后,整个支付完成,会得到一个charge.succeeded的状态。

使用 webhooks

Webhooks 里提供了几十种状态,所有这些状态都会注册到Stripe里一个叫webhooks事件钩子的地方,我们可以指定不同事件的触发时,转发数据到某个我们自己搭建好的Web Api上。(下图是我们的服务器end point, 因为我们没有用到服务器,使用的是亚马逊lambda做一个Serverless)
image

举个支付宝的栗子

服务端 (Serverless)

以AWS的Lambda + API gateway为例, 其中,前者是用来定义API, 后者是做路由。
image

image

image

创建Charge代码:

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

const stripe = require('stripe')(process.env.STRIPE_SECRET_KEY);
exports.handler = (event, context, callback) => {
console.log("request: " + JSON.stringify(event));

let stripeData = event.data.object;
stripe.charges.create({
amount: stripeData.amount,
source: stripeData.id,
currency: stripeData.currency || 'usd',
description: 'My Englishtutor 30 days' || ('Stripe payment ' + event.id),
}, function(err, charge) {
if (err && err.type === 'card_error') {
context.fail(new Error(err.message));
}
else if (err) {
context.fail(err);
}
else {
context.succeed({ status: charge.status, success: true });
}
});
};

客户端 (Web)

多种实现方式:

Checkout

文章开头那段

的集成代码就是使用的checkout方式,非常简单。集成代码直接帮你完成了客户端的部分,服务端只需要定义好source.chargeable的钩子API 就好了。

在做支付宝开发的时候,发现可以直接使用Checkout的方式:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<form action="https://xxx.execute-api.eu-central-1.amazonaws.com/stripepayment/xxx" method="POST">
<script
src="https://checkout.stripe.com/checkout.js" class="stripe-button"
data-key="pk_test_xxx"
data-amount="30000"
data-name="myenglishtutor.eu"
data-label="Pay With Alipay"
data-description="30 days"
data-image="/images/logo.png"
data-locale="auto"
data-alipay="auto"
data-currency="usd">
</script>
</form>

但是总是得到这个错误:

1
Unrecognized request URL (POST: /v1/alipay/send_sms). Please see https://stripe.com/docs or we can help at https://support.stripe.com/.

image

发邮件给Stripe support team得到的结果是为了以后的扩展,Stripe不再提供alipay的checkout方式, 无奈,只得使用下面的方式。

Stripe.js & Elements

当然对于如果你觉得Checkout的方式集成度太高,不够灵活,那Stripe.js是你最好的选择。

Stripe.js其实就是客户端的一个JS核心类库,Elements是它的UI类库,其实上面的Checkout代码就是Stripe使用两者给我们封装好了的,避免我们直接接触敏感信息,但是其实质都是一样的,都是用来创建source。这里就直接贴出客户端的代码了(这里没有用到Elements做UI,因为就是一个按钮支付,太简单,所以没用到):

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
var stripe = Stripe('pk_live_xxxx');

function alipay(amount) {
showLoading();
stripe.createSource({
type: 'alipay',
amount: parseInt(amount),
currency: 'gbp', // usd, eur,
redirect: {
return_url: 'https://xxx.eu/pay/result.html'
},
}).then(function (response) {
hideLoading();
if (response.error) {
alert(response.error.message);
}
else {
processStripeResponse(response.source);
}
});
}

function processStripeResponse(source) {
window.location.href = source.redirect.url;
}

image

这里需要注意几点:

  • currency 必须是Stripe账号所在地货币,也就是绑定的银行卡所在地,因为Jason是英国人,所以必须使用gbp(这里愚蠢如我的犯了一个常识错误,一直以为英国也是欧盟的,所以使用eur,结果怎么也不对,直接哭晕在厕所)
  • return_url指向的是当用户重定向到我们常见的支付宝支付页面后,跳转回支付完成的页面,在这个返回页面中,因为支付宝是同步完成支付的,所以我们可以去查询charge.succeeded的状态来判定是否用户支付是否完成。

当一切OK,点击支付按钮,就会跳转到支付宝支付页面(其他支持的三方平台也可以),如下:
image

微信实现

其实也非常的简单,只需要将上一步的type改为wechat,同时返回source中的source.wechat.qr_code_url转为二维码就好了

1
2
3
4
5
6
7
8
9
10
11
12
var wechatCallback = function (source) {
generateQRCode(source.wechat.qr_code_url);
}
function generateQRCode(value) {
var qrEle = document.getElementById("qrcode");
var qrcode = new QRCode(qrEle, {
width: 100,
height: 100
});
qrcode.makeCode(value);
qrEle.style.display = 'inline-block';
}

二维码出来后, 扫码就会得到如下结果
image

(更新) 本人开发了一款wordpress stripe插件,主要针对微信和支付宝,涉及一些更新,详见另一篇文章Wordpress 插件 Stripe Express 发布啦!

Top

成都持续交付大会

抽空记录一下2017年11月18日中国第十三届持续交付大会的内容,总的来说收货满满,扫盲了很多持续集成,持续交付以及众多分布式相关的技术。

image

关键点

微服务架构

微服务毫无疑问是这两年最火的架构模式之一,可惜没有机会实践,但是了解到的是重点在于如何划分服务,更多的可以参考cnblogs上的一篇博文,很全。
image

京东架构演变

听完演讲者的京东架构演变的过程充分验证了金字塔不是一天建成的,互联网产品一直是迭代的,两张京东架构图足以说明他们的技术演变过程:

架构1.0:
image

架构3.0:
image

Serverless

感觉Serverless 会是另一个未来软件开发的趋势,自己的博客系统里就集成了很多serverless的服务,对于我来说最常见的可能就是类似各种评论系统像DISQUS,多说, (哎,可惜已经关了),存储系统等。当然对企业来说的话,像亚马逊的Lambda这种更能方便客户快速拼接出各种产品,减少开发人员成本。

这个月也参加了亚马逊用户社区活动,里面更是重点提到了Serverless的广泛运用,听完更是为Severless 打call。

扫盲篇(未完):

elasticsearch

jmq

binlog

数据库分库分表(取模)

jproxy

服务找寻(取模)

TRUNKED BASED DEVELOPMENT

A/B 测试

蓝绿部署

分布式调用链追踪原理:在相互每个服务请求header中添加trackId

契约测试框架:janus, pact, pacto, spring cloud contract

开发环境区分:dev-qa-sys-perf-uat-prod (我们公司使用的有点不一样localdev-stage-dev-qa-uat-prod)

Top
Top

windows 下搭建https + node.js + nginx

最近做一个微信小程序的时候因为要求所有请求都得是https的连接,服务器端https 倒是搭建好了,可本地测试没法进行啊,于是只能自己在本地搭建个https的服务。步骤很少,和把大象放进冰箱需要的步骤一样!只需要三步:第一步:要使用ssl,肯定需要生成证书,这里我就生成的自签名证书。 第二部:安装nginx和配置ssl。第三部:用nginx反向代理到node服务端口

Read More

Top
Top