博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
bitcoin_使用Node,Coinbase,Bitcoin和Okta构建自己的发票服务
阅读量:2512 次
发布时间:2019-05-11

本文共 33398 字,大约阅读时间需要 111 分钟。

bitcoin

I got into Bitcoin back in 2011. Since then, I've been a fan of cryptocurrencies and have always had an interest in them. I've also built several Bitcoin projects over the years (an information website, an ecommerce site, and several others) to help promote the usage of the cryptocurrency (while having some fun).

我从2011年开始涉足比特币。从那时起,我一直是加密货币的粉丝,并且一直对它们感兴趣。 多年来,我还建立了几个比特币项目(一个信息网站,一个电子商务网站以及其他几个项目),以帮助促进加密货币的使用(同时获得一些乐趣)。

The idea of being able to send and receive money almost instantly from anywhere in the world with no middleman is really appealing to a lot of people.

几乎没有中间人就可以从世界任何地方即时发送和接收资金的想法确实吸引了很多人。

Today, I thought it'd be fun to build a small web invoicing portal (something similar to , but much less sophisticated) that allows you to easily invoice your clients over email and collect payment in Bitcoin.

今天,我认为建立一个小型的网络发票门户网站(类似于 ,但要复杂得多)会很有趣,它使您可以轻松地通过电子邮件向客户发票并以比特币付款。

The client can then pay their invoices using their local currency or Bitcoin (if they have it). In the end: you'll be able to manage and bill your clients and receive payment in Bitcoin.

然后,客户可以使用当地货币或比特币(如果有)支付发票。 最后:您将能够管理客户并为其开账单,并接收比特币付款。

I do a bit of consulting work myself and will be using this in the future =)

我自己做了一些咨询工作,将来会使用它=)

PS: If you want to skip the article and go , go for it! I'm using Node.js, Express.js, and to power the application.

PS :如果您想跳过本文, 去看 ,那就去做吧! 我正在使用Node.js,Express.js和来为应用程序供电。

Crypto Invoicer

( )

Before I walk you through building the application, there are a few things you'll need to do.

在逐步指导您构建应用程序之前,您需要做一些事情。

You'll need to go create an account with . Coinbase is the largest and most popular Bitcoin exchange in the US. It allows you to easily get started using Bitcoin without needing to install software, learn a lot, etc.

您需要使用创建一个帐户。 Coinbase是美国最大,最受欢迎的比特币交易所。 它使您可以轻松开始使用比特币,而无需安装软件,学习很多东西等。

You'll also need to create an . Okta is an API service that allows you to create user accounts, and perform simple authentication and authorization for your web apps, mobile apps, and API services.

您还需要创建 。 Okta是一项API服务,可让您创建用户帐户,并对Web应用程序,移动应用程序和API服务执行简单的身份验证和授权。

Finally, you'll need to have Node.js setup on your computer and be ready to do some coding! >:)

最后,您需要在计算机上安装Node.js并准备进行一些编码! > :)

设置Coinbase (Set Up Coinbase)

To send invoices and request money from different clients you might be consulting for, you'll need to first generate a Coinbase API key with the proper permissions.

要向可能要咨询的其他客户发送发票并要求付款,您需要首先生成具有适当权限的Coinbase API密钥。

Coinbase has an expansive API that you can use to do a number of things: one of which is . To do this, you'll need to visit the Coinbase , then click the button to create a new API key.

Coinbase有一个可扩展的API,您可以使用它来做很多事情:其中之一是 。 为此,您需要访问Coinbase ,然后单击按钮创建一个新的API密钥。

When you see the popup modal that prompts you for permissions, use the settings below:

当您看到提示您输入权限的弹出模式时,请使用以下设置:

Coinbase API Key Optoins

What you're doing here is requesting API permission to:

您在这里所做的是请求API权限以:

  • View your different Coinbase accounts (wallet:accounts:read)

    查看您不同的Coinbase帐户(钱包:帐户:已读)
  • View any past transactions you've made (wallet:transactions:read)

    查看您过去进行的所有交易(电子钱包:交易记录:已读)
  • Create new transactions to request money (wallet:transactions:request)

    创建新交易以请求付款(电子钱包:transactions:request)

Once you've finished creating the key, you'll be able to see an API key and API secret value. Copy these down, you'll need them later.

完成创建密钥后,您将能够看到API密钥和API机密值。 将它们复制下来,以后再用。

设置Okta (Set Up Okta)

Now that your Coinbase account is ready for usage, you need to set up your Okta account. This is what you'll be using to protect your portal so only you can access it.

现在您的Coinbase帐户已经可以使用了,您需要设置Okta帐户。 这就是用来保护门户的内容,因此只有您可以访问它。

Log into your Okta dashboard and copy down the Org URL value you see at the top right of the page. You will need this value later. It looks something like this:

登录到Okta仪表板,然后复制您在页面右上方看到的组织URL值。 您稍后将需要此值。 看起来像这样:

Okta Org URL

You next need to create a new Okta Application. Using Okta, you can manage users for many applications you might have.

接下来,您需要创建一个新的Okta应用程序。 使用Okta,您可以管理可能拥有的许多应用程序的用户。

To do this, click the large Applications menu item and click Add Application. Then when you are prompted, select the "Web" application option. This tells Okta that you are building a web application (not an API service, for instance). Behind the scenes, Okta uses this information to set your app up with the proper types of OAuth 2.0 and OpenID Connect.

为此,单击大型应用程序菜单项,然后单击添加应用程序。 然后,当系统提示您时,选择“ Web”应用程序选项。 这告诉Okta您正在构建Web应用程序(例如,不是API服务)。 在幕后,Okta使用此信息通过适当类型的OAuth 2.0和OpenID Connect来设置您的应用。

Now you'll see a page asking you to define your Application settings. Use the values below:

现在,您将看到一个页面,要求您定义应用程序设置。 使用以下值:

Okta Create App

These settings basically tell Okta where your web app will be running (locally in this example) and what sort of security rules to apply.

这些设置基本上告诉Okta您的Web应用程序将在何处运行(在此示例中为本地)以及要应用哪种安全规则。

Once you've finished creating the Application, you'll then be taken to your settings page for this newly created Application. You'll want to copy down two values, your Client ID and Client Secret. These will be needed later.

创建完应用程序后,您将转到此新创建的应用程序的设置页面。 您需要复制两个值,即Client IDClient Secret这些将在以后需要。

Okta App Credentials

These credentials will be used to communicate securely with Okta in order to authenticate yourself into the web portal later.

这些凭据将用于与Okta安全通信,以便以后向Web门户进行身份验证。

( )

Now that we've done the boring stuff, let's take a look at some code.

现在,我们已经完成了无聊的工作,让我们看一些代码。

You can either clone the project locally from my Github repository:

您可以从我的Github存储库本地克隆项目:

$ git clone https://github.com/rdegges/crypto-invoicer

Or you can to your own Github account and then clone that locally. This might make it easier to make changes and play around with the code as you follow along below.

或者,您可以到您自己的Github帐户,然后在本地克隆该帐户。 您可以按照以下步骤更轻松地进行更改和使用代码。

Through the rest of this article, I'll assume that you're working inside of the cloned/forked project directory.

在本文的其余部分中,我假定您正在克隆/分叉的项目目录中进行工作。

( )

Now let's take the credentials you gathered earlier and define them as environment variables that you'll use to store these sensitive values.

现在,让我们采用您之前收集的凭据,并将其定义为环境变量,您将使用它们存储这些敏感值。

To do this, you'll want to create a file named .env which looks like the following:

为此,您需要创建一个名为.env的文件,该文件如下所示:

# .envexport OKTA_ISSUER_URI=https://xxx/oauth2/defaultexport OKTA_CLIENT_ID=xxxexport OKTA_CLIENT_SECRET=xxxexport REDIRECT_URI=http://localhost:3000/authorization-code/callbackexport PORT=3000export SECRET=xxxexport COINBASE_APIKEY_ID=xxxexport COINBASE_APIKEY_SECRET=xxx

Substitute your credentials where you see the xxx placeholder:

在看到xxx占位符的地方替换您的凭据:

  • OKTA_ISSUER_URI should be set to the value of the Org URL value you copied down earlier, and placed into the URL. The final URL should look something like https://dev-111464.oktapreview.com/oauth2/default.

    OKTA_ISSUER_URI应该设置为您先前复制下来的组织URL值的值,并放入URL中。 最终网址应类似于https://dev-111464.oktapreview.com/oauth2/default
  • OKTA_CLIENT_ID and OKTA_CLIENT_SECRET are the Application credentials you generated when you created your Okta Application previously

    OKTA_CLIENT_IDOKTA_CLIENT_SECRET是您先前创建Okta应用程序时生成的应用程序凭据
  • REDIRECT_URI is a hard-coded URL that will be used as part of the authentication flow. More on this later.

    REDIRECT_URI是一个硬编码的URL,将用作身份验证流的一部分。 稍后再详细介绍。
  • PORT is the HTTP port you'll be running your webserver on. 3000 is standard for Node.js apps

    PORT是您将在其上运行Web服务器的HTTP端口。 3000是Node.js应用程序的标准配置
  • SECRET should be a long, random string you define. This is used to secure your HTTP sessions and keep your authentication data safe. I like to generate these by bashing my hands on the keyboard for a second or two.

    SECRET应该是您定义的长随机字符串。 这用于保护HTTP会话并确保身份验证数据的安全。 我喜欢通过在键盘上踩一下手一两秒钟来生成这些内容。
  • COINBASE_APIKEY_ID and COINBASE_APIKEY_SECRET are your Coinbase API credentials

    COINBASE_APIKEY_IDCOINBASE_APIKEY_SECRET是您的Coinbase API凭据

Once you have these settings defined, you'll need to tell your terminal to use these variables. To do this, if you're using a standard Linux/Mac/BSD terminal, you can run the command:

一旦定义了这些设置,就需要告诉终端使用这些变量。 为此,如果您使用的是标准Linux / Mac / BSD终端,则可以运行以下命令:

$ source .env

The source command will tell your shell to take the variables defined in this file and make them available to the terminal for usage in your programs later on.

source命令将告诉您的Shell使用此文件中定义的变量,并使它们可供终端使用,以便以后在程序中使用。

If you're using Windows, you'll need to . Sorry!

如果您使用的是Windows,则需要 。 抱歉!

( )

Now that the setup is completely finished, install all of the project dependencies using npm, the Node package manager:

现在设置已完全完成,请使用Node软件包管理器npm安装所有项目依赖项:

$ npm install

This command will install all of the dependent packages by analyzing the package.json and package-lock.json file in the project directory.

该命令将通过分析项目目录中的package.jsonpackage-lock.json文件来安装所有依赖包。

Among these dependencies, there are a few interesting ones:

在这些依赖项中,有一些有趣的依赖项:

  • is the web framework you'll use to build the app

    是用于构建应用程序的Web框架
  • is the officially supported Coinbase developer library you'll be using to interact with the Coinbase API

    是官方支持的Coinbase开发人员库,您将使用该库与Coinbase API进行交互
  • is a popular OpenID Connect middleware maintained by Okta that handles user authentication and authorization for Node apps

    是Okta维护的一种流行的OpenID Connect中间件,用于处理Node应用程序的用户身份验证和授权

( )

Fair warning: I'm not a great frontend developer. I'm more of a server-side developer.

合理的警告:我不是一个出色的前端开发人员。 我更多地是服务器端开发人员。

The first thing I like to do when starting new projects is quickly define the frontend views. This part is more difficult for me, so I like to get it out of the way upfront.

启动新项目时,我想做的第一件事就是快速定义前端视图。 这部分对我来说比较困难,因此我喜欢先解决它。

If you take a look at the views directory, you'll notice that there are only three files: base.pug, index.pug, and dashboard.pug. These three views render the entire website.

如果查看views目录,您会注意到只有三个文件: base.pugindex.pugdashboard.pug 。 这三个视图呈现整个网站。

  • base.pug is a shared base template that the other two templates extend. More on this in a moment.

    base.pug是其他两个模板扩展的共享基础模板。 稍后对此进行更多讨论。
  • index.html is the home page of the site

    index.html是网站的主页
  • dashboard.pug is the site's dashboard view

    dashboard.pug是网站的仪表板视图

I've defined these HTML views using the templating language. This lets you write HTML without all the closing tags and allows you to use whitespace to infer structure.

我已经使用模板语言定义了这些HTML视图。 这样一来,您无需编写所有结束标记就可以编写HTML,并可以使用空格来推断结构。

The base.pug template provides some common HTML that the other two views extend. This prevents you from needing to duplicate HTML that is shared between one or more pages.

base.pug模板提供了其他两个视图扩展的一些常见HTML。 这样可以避免您需要复制一个或多个页面之间共享HTML。

Here's what the base.pug template looks like:

这是base.pug模板的样子:

doctype htmlhtml(lang="en")  head    
meta(charset="utf-8") meta(name="viewport", content="width=device-width, initial-scale=1, shrink-to-fit=no")
link(rel="stylesheet", href="https://bootswatch.com/4/sketchy/bootstrap.min.css") link(rel="stylesheet", href="/static/css/style.css") body .container block body
script(src="https://code.jquery.com/jquery-3.2.1.slim.min.js", integrity="sha384-KJ3o2DKtIkvYIK3UENzmM7KCkRr/rE9/Qpg6aAZGJwFDMVNA/GpGFF93hXpG5KkN", crossorigin="anonymous") script(src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.12.3/umd/popper.min.js", integrity="sha384-vFJXuSJphROIrBnz7yo7oB41mKfc8JzQZiCq4NCceLEaO4IHwicKwpJf9c9IpFgh", crossorigin="anonymous") script(src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-beta.2/js/bootstrap.min.js", integrity="sha384-alpBpkh1PFOepccYVYDB4do5UnbKysX5WZXm3XxPqe5iKTfUKjNkCk9SaVuEZflJ", crossorigin="anonymous")

This is a pretty standard HTML page that uses the CSS library with the Bootswatch theme. This theme makes the entire site look like a mockup. Since this is an example application, I thought the theme was fitting.

这是一个非常标准HTML页面,它使用带有 Bootswatch主题的 CSS库。 这个主题使整个网站看起来像一个模型。 由于这是一个示例应用程序,因此我认为主题很合适。

The index.pug view is also quite simple:

index.pug视图也非常简单:

extends base.pugblock body  h1.text-center.head Crypto Invoicer  .row.intro    .col    .col-8      .jumbotron        h2.text-center A Personal Invoicing Portal        p.          This is a personal invoicing portal. Use this portal to bill your clients          for work done using #[a(href="https://www.coinbase.com/") Coinbase]:          accept money in USD or Bitcoin.        p.          If you're performing work for clients and need a simple way to bill              them, give Crypto Invoicer a try!        p.          Please #[a.btn.btn-primary(href="/login") login] to continue.    .col

This template simply displays a basic home page that prompts the user to log into their account to continue:

此模板仅显示一个基本主页,提示用户登录其帐户以继续:

Crypto Invoicer

The last view you need to inspect is the dashboard.pug view. This view renders the dashboard page that allows a user to create and view their invoices.

您需要检查的最后一个视图是dashboard.pug视图。 该视图呈现仪表板页面,该页面允许用户创建和查看其发票。

extends base.pugblock body  script(src="/static/js/sorttable.js")  ul.nav.nav-pills.justify-content-end    li.nav-item      a.nav-link.active(href="/") Home      li.nav-item        a.nav-link.active(href="/logout") Logout  h1.text-center Dashboard  h2.create-invoice Create Invoice  .row    .col    .col-6      .jumbotron        if error          p.error #{error}        form.form(method="post")          .form-group            label(for="email") To            input#email.form-control(type="email", placeholder="Client Email", name="email", required=true)          .form-group            label(for="description") Description            input#description.form-control(type="text", placeholder="Description", name="description", required=true)          .form-group            label(for="amount") Amount (USD)            input#amount.form-control(type="number", min="1", step="any", name="amount", required=true)          button.btn.btn-primary(type="submit") Create Invoice    .col  if transactions    h2 Invoices    table.table.sortable      thead.thead-dark        tr          td Invoice #          td Date Created          td Completed?          td Client Email          td Description          td Amount (USD)      tbody        each transaction in transactions          tr            td #{transaction.id}            td #{new Date(transaction.created_at).toLocaleDateString("en-US", { hour12: false, year: "numeric", month: "2-digit", day: "2-digit", hour: "2-digit", minute: "2-digit", second: "2-digit" })}            td #{transaction.status}            td #{transaction.to.email}            td #{transaction.description}            td $#{transaction.native_amount.amount}

This page is a bit more complex. It does a few key things:

该页面稍微复杂一些。 它做了一些关键的事情:

  • It creates a form that allows the user to send a client an invoice. This form takes a few input parameters: the client's email address, a description of what is being billed, and finally an amount (in USD) to bill the client.

    它创建一个允许用户向客户发送发票的表格。 该表格采用了一些输入参数:客户的电子邮件地址,要开具发票的描述以及最终要向客户开具发票的金额(美元)。
  • It lists all past invoices in an HTML table that can be sorted with JavaScript. To do this, you'll use pug to loop through all past transaction objects, and display their data as appropriate.

    它在可以使用JavaScript排序HTML表中列出了所有过去的发票。 为此,您将使用pug遍历所有过去的事务对象,并适当地显示其数据。

When you render this page, you'll see the invoice creation form:

呈现此页面时,您将看到发票创建表单:

Crypto Invoicer Form

And… If you've generated any past invoices, you'll see them listed below:

而且...如果您生成了任何过去的发票,则会在下面列出它们:

Crypto Invoicer List

You'll also notice that if you click one of the table headers, you're able to sort all of the invoices by any column you want.

您还将注意到,如果单击表标题之一,则可以按所需的任何列对所有发票进行排序。

If you take a look at the dashboard.pug template code above, you can see how this works:

如果看一下上面的dashboard.pug模板代码,您将看到它是如何工作的:

  • The JavaScript library is used to provide automatic table sorting in the browser

    JavaScript库用于在浏览器中提供自动表排序
  • Pug is being used to display transaction details

    Pug用于显示交易详细信息

Other than these two things, the rest of the page is plain old HTML. Nothing fancy!

除了这两件事,页面的其余部分是普通的旧HTML。 没有什么花哨!

( )

Now that you've seen how the frontend code works, let's take a look at the server-side codebase.

现在您已经了解了前端代码的工作原理,让我们看一下服务器端代码库。

Open up the server.js file found in the root of the project folder and follow along below.

打开在项目文件夹的根目录中找到的server.js文件,并按照下面的步骤进行操作。

导入依赖 (Import Dependencies)

The first thing I do in the server.js is import all the Node.js dependencies needed to run the app:

我在server.js所做的第一件事是导入运行该应用程序所需的所有Node.js依赖项:

"use strict";const Client = require("coinbase").Client;const async = require("async");const bodyParser = require("body-parser");const express = require("express");const session = require("express-session");const ExpressOIDC = require("@okta/oidc-middleware").ExpressOIDC;

Nothing special here! Importing dependencies is standard in just about every app.

这里没什么特别的! 几乎每个应用程序都导入依赖项。

定义全局 (Define Globals)

The next thing you'll notice in server.js is a section of code that defines a number of global variables:

您将在server.js注意到的第二件事是一段代码,它定义了许多全局变量:

// Globalsconst OKTA_ISSUER_URI = process.env.OKTA_ISSUER_URI;const OKTA_CLIENT_ID = process.env.OKTA_CLIENT_ID;const OKTA_CLIENT_SECRET = process.env.OKTA_CLIENT_SECRET;const REDIRECT_URI = process.env.REDIRECT_URI;const PORT = process.env.PORT || "3000";const SECRET = process.env.SECRET;const client = new Client({
apiKey: process.env.COINBASE_APIKEY_ID, apiSecret: process.env.COINBASE_APIKEY_SECRET});let account;let transactions;let app = express();

All the const definitions are fairly straightforward: they pull in the environment variable values that were set previously, and store them as JavaScript variables so they can be easily referenced.

所有const定义都非常简单:它们引入了先前设置的环境变量值,并将它们存储为JavaScript变量,以便可以轻松地引用它们。

The client variable defines a new Coinbase API client (which is later used to talk to the Coinbase API).

client变量定义了一个新的Coinbase API客户端(以后用于与Coinbase API通讯)。

The account variable represents a Coinbase Account object. In Coinbase you can have any number of "Accounts": Bitcoin wallets, USD wallets, etc. You can move money between these much like checking accounts at a normal bank. When implementing the invoicing later, you'll need to know which Coinbase Account you want to issue the request for, this determines how you receive the money.

account变量代表一个Coinbase Account对象。 在Coinbase中,您可以有任意数量的“帐户”:比特币钱包,美元钱包等。您可以在这两者之间转移资金,就像在普通银行中检查支票一样。 稍后实施发票时,您需要知道要发出请求的Coinbase帐户,这决定了您的收款方式。

The transactions variable will be our own in-memory cache of all recent invoice transactions available to us via the Coinbase API. This is what we'll use when rendering our dashboard page later on: we'll store a cache of the transactions to avoid making API calls to Coinbase on each page load.

transactions变量将是我们自己的内存中缓存,其中包含通过Coinbase API提供给我们的所有最近发票交易。 这就是我们稍后在呈现仪表盘页面时将使用的方法:我们将存储事务的缓存,以避免在每次页面加载时对Coinbase进行API调用。

Finally, you'll notice the app variable. This is a standard Express.js convention: create an app object and use that to start up the web server later on.

最后,您会注意到app变量。 这是Express.js的标准约定:创建一个app对象,然后使用该对象启动Web服务器。

配置应用程序设置和中间件 (Configure App Settings and Middleware)

Once the globals have been defined, the next thing you need to do is define the app settings and middleware.

定义了全局变量后,接下来要做的就是定义应用程序设置和中间件。

There's a section of code commented that contains these two blocks of functionality:

有一段注释的代码包含以下两个功能块:

// App settingsapp.set("view engine", "pug");// App middlewareapp.use("/static", express.static("static"));app.use(session({
cookie: {
httpOnly: true }, secret: SECRET}));// Authenticationlet oidc = new ExpressOIDC({
issuer: OKTA_ISSUER_URI, client_id: OKTA_CLIENT_ID, client_secret: OKTA_CLIENT_SECRET, redirect_uri: REDIRECT_URI, routes: {
callback: {
defaultRedirect: "/dashboard" } }, scope: "openid profile"});app.use(oidc.router);

There's only one actual app setting here: app.set("view engine", "pug");, and all it does is tell Express to use the pug templating engine when rendering views.

这里只有一个实际的应用程序设置: app.set("view engine", "pug"); ,它所做的就是告诉Express在渲染视图时使用pug模板引擎。

Below that are the middleware definitions.

下面是中间件的定义。

The first middleware defined is express.static. This middleware is configured to serve static assets (css, images, javascript) from the static directory in the root of the project folder. This definition tells Express that any requests that start with /static should be routed to that folder, and automatically return whatever files exist there.

定义的第一个中间件是express.static 。 该中间件配置为从项目文件夹根目录中的static目录提供静态资产(css,图像,javascript)。 此定义告诉Express,所有以/static开头的请求都应路由到该文件夹​​,并自动返回该文件夹中存在的所有文件。

This might be a good time to inspect the static folder and see what's in it. There are only two files:

这可能是检查static文件夹并查看其中内容的好时机。 只有两个文件:

  • A style.css file which holds some custom styling, and

    一个style.css文件,其中包含一些自定义样式,以及
  • A sorttable.js script which is used in our frontend to make our HTML table sortable

    在我们的前端中使用的sorttable.js脚本使我们HTML表可排序

Next you'll see the express-session middleware defined. What this middleware does is configure Express to store sensitive user information in cookies (which are the safest way to store authentication data). When you are logged into the website via Okta later on, your authentication information will be stored in these cookies that are managed by this library.

接下来,您将看到定义的快速会话中间件。 该中间件所做的是将Express配置为将敏感的用户信息存储在cookie中(这是存储身份验证数据的最安全方法)。 稍后当您通过Okta登录网站时,您的身份验证信息将存储在此库管理的这些cookie中。

NOTE: The SECRET variable that's used when initializing the session library is incredibly important. This long, random string that you previously defined is what keeps your cookies safe from tampering. If this value is ever leaked publicly (on Github, etc.) it would be a security catastrophe. All cookie-based systems require a secret key to be used to cryptographically validate the cookie.

注意 :初始化会话库时使用的SECRET变量非常重要。 您先前定义的这个长而随机的字符串可以防止Cookie被篡改。 如果此值曾经公开泄漏(在Github等上),那将是安全灾难。 所有基于cookie的系统都需要一个密钥,以用于加密验证cookie。

The last middleware you'll see is the oidc-middleware. This is a little more complex, as it handles a lot of magic behind the scenes, and makes all the authentication logic in the application work.

您将看到的最后一个中间件是oidc中间件。 这有点复杂,因为它处理了很多幕后的魔术,并使应用程序中的所有身份验证逻辑起作用。

The way this middleware works is by fully enabling your app to use OpenID Connect (OIDC) for authentication. When you define the new ExpressOIDC and give it your Okta configuration information, it builds an OIDC object that remembers all your application rules: what URL your application runs one, where to redirect the user after they've logged in, what your secret application keys are, etc.

该中间件的工作方式是完全使您的应用程序使用OpenID Connect(OIDC)进行身份验证。 当您定义新的ExpressOIDC并为其提供Okta配置信息时,它将构建一个OIDC对象,该对象将记住您的所有应用程序规则:应用程序运行哪个URL,用户登录后将其重定向到何处,您的秘密应用程序密钥是什么是,等等。

Once this new object is created, it contains an Express router object that is then enabled below with the app.use(oidc.router); call. This line registers some magical routes behind the scenes: the main one of which is /login.

一旦创建了这个新对象,它将包含一个Express路由器对象,然后在下面使用app.use(oidc.router);启用app.use(oidc.router); 呼叫。 该行在幕后记录了一些神奇的路线:其中主要的一条是/login

When this line of code is executed any requests to /login will redirect you to your dedicated login page (hosted by Okta), and prompt you to log into the application. Once the user has been logged in, they will then be redirected BACK to your Node application, where they will be logged in and able to access the dashboard page.

执行此行代码后,对/login任何请求均会将您重定向到专用的登录页面(由Okta托管),并提示您登录该应用程序。 一旦用户登录,他们将被重定向回您的Node应用程序,在那里他们将被登录并能够访问仪表板页面。

定义助手 (Define Helpers)

Let's skip towards the bottom of the server.js file now and take a look at the updateTransactions function:

现在,让我们跳到server.js文件的底部,看一下updateTransactions函数:

// Helpersfunction updateTransactions(cb) {
transactions = []; let pagination = null; async.doWhilst( function(callback) {
account.getTransactions(pagination, (err, txns, page) => {
if (err) {
return callback(err); } pagination = page.next_uri ? page : false; txns.forEach(txn => {
if (txn.type === "request") {
transactions.push(txn); } }); callback(); }); }, function() {
return pagination ? true: false; }, function(err) {
if (err) {
return cb(err); } cb(null, transactions); } );}

The purpose of this helper function is to build an in-memory cache of Coinbase transactions.

这个辅助函数的目的是建立一个Coinbase事务的内存缓存。

Each time you request money from a client and send them an invoice, Coinbase creates a transactional record. There are many different types of transactions that Coinbase logs, so what this function does is iterate through all available transactions, pruning out just the ones relevant to invoicing, then stores them in the global transactions array variable for later usage.

每当您要求客户付款并向他们发送发票时,Coinbase都会创建一个交易记录。 Coinbase记录了许多不同类型的交易,因此该功能的作用是遍历所有可用交易,仅删除与发票相关的transactions ,然后将其存储在全局transactions数组变量中以备后用。

The idea here is that each time the dashboard is displayed, instead of talking to the Coinbase API and performing this logic in real-time, the app will simply pull the latest list of transactions out of the cache instead.

这里的想法是,每次显示仪表板时,该应用程序都不会从Coinbase API进行交谈并实时执行此逻辑,而只是从缓存中提取最新的事务列表。

In this function I'm using the library to perform a do-while loop that:

在此函数中,我使用库执行以下操作的do-while循环:

  • Talks to the Coinbase API and requests a list of transactions

    与Coinbase API对话并请求交易清单
  • Tries to determine whether there are any more "pages" of transactions left to iterate through (because there may be many transactions, it might require many requests to the Coinbase API to retrieve them all)

    尝试确定是否还有剩余的交易“页面”要进行迭代(因为可能有很多交易,因此可能需要向Coinbase API发出很多请求才能全部检索)
  • Filters out only the transactions that are of the type "request", as these are the "request" money transactions that this app generates

    仅筛选出“请求”类型的交易,因为这些是此应用生成的“请求”资金交易
  • Stores them in the global transactions array for later usage

    将它们存储在全局transactions数组中以备后用

定义启动作业 (Define Startup Jobs)

The next thing you'll do is define the jobs that need to run each time this Node server starts up.

接下来要做的是定义每次节点服务器启动时需要运行的作业。

If you take a look at the startup jobs code block, you'll see what I mean:

如果看一下启动作业代码块,您将明白我的意思:

// Startup jobsclient.getAccounts({
}, (err, accounts) => {
if (err) {
console.error(err); } accounts.forEach(acct => {
if (acct.primary) {
account = acct; console.log("Found primary account: " + account.name + ". Current balance: " + account.balance.amount + " " + account.currency + "."); console.log("Downloading initial list of transactions."); updateTransactions(err => {
if (err) {
console.error(err); } }); } });});

What this code does is:

该代码的作用是:

  • Use the Coinbase API to list all Accounts (these are the places you can store money in Coinbase)

    使用Coinbase API列出所有帐户(这些是您可以在Coinbase中存钱的地方)
  • Look through each Account until it finds the primary (this is usually your Bitcoin wallet used to store Bitcoin)

    浏览每个帐户,直到找到主要帐户(通常是您用来存储比特币的比特币钱包)
  • Sets the global account variable to this value

    将全局account变量设置为此值

Then, once the proper Account object has been found, this code will execute the updateTransactions helper function defined earlier, to build the initial in-memory cache of transactions.

然后,一旦找到正确的Account对象,此代码将执行前面定义的updateTransactions帮助器函数,以建立事务的初始内存缓存。

This way, shortly after the web server starts running all transaction data will be available for querying.

这样,在Web服务器开始运行后不久,所有事务数据将可用于查询。

定义服务器管理代码 (Define Server Management Code)

Towards the bottom of the server.js file you'll see a few things:

server.js文件的底部,您将看到一些信息:

// Cron jobssetInterval(() => {
updateTransactions(err => {
if (err) {
console.error(err); } })}, 1000 * 60 * 60);// Server managementoidc.on("ready", () => {
app.listen(PORT);});oidc.on("error", err => {
console.error(err);});

The setInterval() call essentially tells this Node process to update the cache of transaction data once per hour (in milliseconds). This way, all transaction information will be at most one hour old.

setInterval()调用实质上告诉此Node进程每小时更新一次事务数据的缓存(以毫秒为单位)。 这样,所有交易信息最多只能使用一小时。

Finally, the Express app itself will be launched once the authentication library has finished preparing itself.

最后,一旦身份验证库准备就绪,Express应用程序本身将启动。

NOTE: It's important not to run the web server (app.listen(PORT);) until the OIDC library emits the "ready" event. This could result in odd security edge cases where a user making requests to protected pages on your website runs into errors if they make a request before the OIDC library has finished configuring itself.

注意 :在OIDC库发出“就绪”事件之前,不要运行Web服务器( app.listen(PORT); ),这一点很重要。 这可能会导致奇怪的安全边缘情况,即如果用户在OIDC库完成自身配置之前提出请求,则对您网站上受保护页面的请求会出错。

创建路线 (Create Routes)

Now that we've gone through the rest of the server.js code, let's look at the final section we skipped from earlier (the routes):

现在,我们已经完成了server.js代码的其余部分,让我们看一下从先前(路由)中跳过的最后一部分:

// App routesapp.get("/", (req, res) => {
res.render("index");});app.get("/dashboard", oidc.ensureAuthenticated(), (req, res) => {
res.render("dashboard", {
transactions: transactions });});app.post("/dashboard", oidc.ensureAuthenticated(), bodyParser.urlencoded(), (req, res) => {
account.requestMoney({
to: req.body.email, amount: req.body.amount, currency: "USD", description: req.body.description }, (err, txn) => {
if (err) {
console.error(err); return res.render("dashboard", {
error: err }); } updateTransactions((err, transactions) => {
if (err) {
console.error(err); return res.render("dashboard", {
error: err.message }); } return res.render("dashboard", {
transactions: transactions }) }); });});app.get("/logout", (req, res) => {
req.logout(); res.redirect("/");});

The first route just displays the home page of the site. Since all we need here is to show a simple template, there's nothing special we need to do other than render the page.

第一条路线仅显示站点的主页。 由于这里我们只需要显示一个简单的模板,因此除了呈现页面之外,我们不需要做其他特别的事情。

The app.get("/dashboard") route is what displays the dashboard page when requested. What's important to note here is the additional middleware it uses: oidc.ensureAuthenticated(). This middleware forces the user to log in before being able to access this page.

app.get("/dashboard")路由是在被请求时显示仪表板页面的路径。 这里要注意的重要一点是它使用的其他中间件: oidc.ensureAuthenticated() 。 该中间件强制用户登录后才能访问该页面。

If you try to visit the /dashboard page before logging in, for instance, you'll be redirected to the login page and forced to authenticate.

例如,如果您尝试在登录前访问/dashboard页面,则会将您重定向到登录页面并被强制进行身份验证。

Once the user has authenticated, however, they'll be allowed into the dashboard page, which simply renders itself using the in-memory cache of transaction data.

但是,一旦用户通过身份验证,就将允许他们进入仪表板页面,该页面仅使用事务数据的内存高速缓存进行呈现。

The app.post("/dashboard") route is what handles the invoicing.

app.post("/dashboard")路线是处理发票的方式。

When a user fills out the invoice form and clicks "submit", this route is processed and receives the invoicing data. It then talks to Coinbase using the Coinbase API and generates a proper money request (and email). Finally, before refreshing the page and showing the new list of transactions, this code will force a refresh of the transaction data cache.

当用户填写发票表格并单击“提交”时,将处理此路径并接收发票数据。 然后,它使用Coinbase API与Coinbase对话,并生成适当的付款请求(和电子邮件)。 最后,在刷新页面并显示新的事务列表之前,此代码将强制刷新事务数据缓存。

By forcing the cache refresh after each new invoice is created, this prevents an issue where after creating an invoice you would not see it appear in the list below.

通过在创建每个新发票后强制刷新缓存,可以防止出现以下问题:创建发票后,您不会看到它出现在下面的列表中。

When invoices are generated and Coinbase sends out an email, the client receives an email that looks something like this:

生成发票并Coinbase发送电子邮件时,客户端会收到一封类似于以下内容的电子邮件:

Crypto Invoicer Email

This is quite nice, because then a click can simply click the "Complete this payment." button, and be redirected to Coinbase where they can complete the transaction using either Bitcoin or their local currency (USD) to pay.

这非常好,因为单击即可简单地单击“完成此付款”。 按钮,然后重定向到Coinbase,他们可以在其中使用比特币或当地货币(USD)进行支付。

( )

As I've hopefully shown you, building Bitcoin invoicing software using Node.js can be fairly straightforward.

正如我希望向您展示的那样,使用Node.js构建比特币发票软件可能非常简单。

The Coinbase API provides a lot of rich functionality. Paired with Okta for authentication and several open source Node.js libraries, you can quickly throw together complicated applications in a small amount of time.

Coinbase API提供了许多丰富的功能。 与Okta进行身份验证以及几个开源Node.js库一起使用,您可以在短时间内Swift将复杂的应用程序组合在一起。

If you're interested in building cryptocurrency apps of your own, I highly recommend you create a and check out their fantastic . They have a good number of libraries and tools available to help you build your applications in a fun and fast way.

如果您有兴趣构建自己的加密货币应用程序,强烈建议您创建一个并查看其出色的 。 它们具有大量可用的库和工具,可帮助您以有趣而快速的方式构建应用程序。

I'd also recommend creating an which you can use to store users for your web apps, mobile apps, and API services, as well as handle authentication, authorization, OAuth2, OpenID Connect, Single Sign-On, etc.

我还建议创建一个 ,该可用于存储您的Web应用程序,移动应用程序和API服务的用户,以及处理身份验证,授权,OAuth2,OpenID Connect,单一登录等。

Finally, if you'd like to see more articles like this, tweet and let me know! We've also got a ton of other interesting developer articles you can find on the .

最后,如果您想看更多类似的文章,请发鸣叫,让我知道! 您还可以在上找到大量其他有趣的开发人员文章。

翻译自:

bitcoin

转载地址:http://nruwd.baihongyu.com/

你可能感兴趣的文章
手把手教你使用markdown
查看>>
Linux系统安装jdk——.tar.gz版(old)
查看>>
babylin使用思路
查看>>
laravel生命周期
查看>>
阿里面试100%问到,JVM性能调优篇
查看>>
Android用户界面
查看>>
Python 日期时间处理
查看>>
图像处理中的 通道, 深度
查看>>
scons编译mongodb(vs2008版本)遇到的问题总结
查看>>
02_运算符与分支
查看>>
SQL面试题集合
查看>>
多实例设置本地IP访问sql server 数据库
查看>>
matplotlib制图——饼状图
查看>>
支付宝架构师眼中的高并发架构——1
查看>>
Ubuntu 16.04 同时使用python3.5
查看>>
各种标准文档格式的解析工具
查看>>
iOS 8 通知设置页找不到App的问题
查看>>
new Date()的浏览器兼容性问题
查看>>
小技巧1-最大差
查看>>
dp、sp 、 px之间的相互转化的工具类
查看>>