使用React和Blockstack構(gòu)建去中心化Todolist

原文:https://dev.to/dkb868/build-a-decentralized-todo-list-with-react-and-blockstack-59cm

沒翻譯完,請看 http://www.reibang.com/p/078c1dae4397

在線例子:
https://rebase-todolist.netlify.com

npm run eject


在本教程中,你將學(xué)習(xí)使用Blockstack和React構(gòu)建去中心化的Todolist拐辽。Blockstack是一個平臺梨州,它使構(gòu)建Dapp變得非常容易,與傳統(tǒng)的auth/storage方法相比详囤,使用Blockstack身份驗證和存儲構(gòu)建一個簡單的App更快、更安全。

Blockstack的去中心化方法

像Google和Facebook這樣的大公司都有中心化的數(shù)據(jù)庫巡球,它們可以控制你的數(shù)據(jù),并且可以對數(shù)據(jù)做任何他們想做的事情邓嘹。

Blockstack App允許用戶完全控制自己的數(shù)據(jù)酣栈。沒有用戶的允許,任何人都不能訪問用戶的數(shù)據(jù)汹押。用戶數(shù)據(jù)被加密并存儲在私人的“數(shù)據(jù)鎖”中矿筝,用戶可以給App權(quán)限來讀取或?qū)懭霐?shù)據(jù)到他們的存儲中。

在我們的Todolist App中棚贾,這意味著App開發(fā)人員永遠不會知道你的Todolist上有什么窖维。

The App

我們的Todolist將非常簡單,這樣我們就可以專注于學(xué)習(xí)Blockstack是如何工作的妙痹。

完成后的App是這個樣子:

Todolist App

demo 網(wǎng)址: https://blockstack-todo-list.netlify.com/

Github 網(wǎng)址: https://github.com/dkb868/secure-todo-list

The Setup

首先铸史,我們將設(shè)置環(huán)境,安裝node.js的最新版本怯伊。

React

我們使用 create-react-app, 在你命令行窗口輸入 npx create-react-app secure-todo-list 創(chuàng)建新的項目琳轿。
大約一分鐘后,就應(yīng)該完成了耿芹。

控制臺

進入項目 cd secure-todo-list 然后輸入 npm start 啟動項目

Then open up the project folder in your coding editor and let's do some cleanup. Delete the following files:

App.css
App.test.js
index.css
logo.svg

然后打開App.js崭篡,將內(nèi)容替換為:

import React from "react"

class App extends React.Component {
  render() {
    return <div>Nice Meme</div>
  }
}

export default App

并更新 index.js

import React from "react"
import ReactDOM from "react-dom"
import App from "./App"
import * as serviceWorker from "./serviceWorker"

ReactDOM.render(<App />, document.getElementById("root"))

// If you want your app to work offline and load faster, you can change
// unregister() to register() below. Note this comes with some pitfalls.
// Learn more about service workers: https://bit.ly/CRA-PWA
serviceWorker.unregister()

Prettier 插件

如果你不使用Prettier,我強烈推薦它猩系。它使你的代碼更干凈媚送。你可以通過尋找Prettier插件將其添加到編輯器中。

.prettierrc文件添加到項目根目錄(secure-todo-list/)寇甸,內(nèi)容為空塘偎,這將提供默認設(shè)置疗涉。
{}

Semantic UI

我們將使用Semantic UI,一個CSS庫吟秩,給我們的應(yīng)用程序一些樣式咱扣。

將這個url (https://cdnjs.cloudflare.com/ajax/libs/semanticui/2.4.1 /semantic.min.css)復(fù)制到你的public/index.html中,方法是將這一行添加到html文件的頭部涵防。

<link
  rel="stylesheet"
  
/>

現(xiàn)在闹伪,你應(yīng)該已經(jīng)完成了一個非常漂亮的、極簡主義的網(wǎng)站壮池。

Blockstack 賬戶

你需要一個Blockstack帳戶 以便你可以登錄和使用App偏瓤。你可以通過https://blockstack.org/并從菜單中選擇Create ID獲取一個賬戶。

Blockstack

一個簡單的 Todo List

首先椰憋,我們用React構(gòu)建一個簡單的Todolist厅克,其中不包含任何Blockstack技術(shù)。每當(dāng)頁面刷新時橙依, App狀態(tài)將丟失证舟,這將使它更容易看到Blockstack的作用。

初始化狀態(tài)

我們添加一些狀態(tài)到我們的App窗骑。在App.js的render函數(shù)上面添加內(nèi)容:

state = {
  todos: [
    {
      id: 1,
      title: "Wash the dishes",
      done: false,
    },
    {
      id: 2,
      title: "Clean my room",
      done: false,
    },
  ],
}

現(xiàn)在我們的應(yīng)用程序跟蹤todos女责,它有三個屬性:

  • id:todo的唯一標(biāo)識符
  • title: 任務(wù)的名稱
  • done:是否已完成這個任務(wù)

顯示Todos

現(xiàn)在我們有了一些todo,讓我們在頁面上顯示它們创译。

改變render方法抵知,代碼如下:

 render() {
    return (
      <div style={{ padding: "30px 0" }}
        className="ui text container center aligned">
        <h2>My Todos</h2>
        <div className="ui grid">
          <div className="row centered">
            <div className="column twelve wide">
              <div className="grouped fields">
                {this.state.todos
                  .filter(todo => !todo.done)
                  .map(todo => (
                    <div key={todo.id} className="field">
                      <div className="ui checkbox">
                        <input type="checkbox" />
                        <label>{todo.title}</label>
                      </div>
                    </div>
                  ))}
              </div>
            </div>
          </div>
        </div>
      </div>
    );
  }

所有的類名,比如 ui text container center aligned昔榴,都來自 Semantic UI辛藻,幫助我們的應(yīng)用程序看起來更好。

這 1 行 this.state.todos.filter(todo => !todo.done).map(todo => ... 過濾掉已經(jīng)完成的待辦事項互订,并將它們隱藏在頁面之外吱肌。

現(xiàn)在有一個看起來像待辦事項列表的東西。

todolist

如果單擊其中一個復(fù)選框仰禽,就會發(fā)現(xiàn)什么也不發(fā)生氮墨。理想情況下,我們想讓東西在檢查時消失吐葵,所以我們把它加進去规揪。

完成 Todos

Add an onClick handler to the checkbox.

在復(fù)選框中添加一個onClick處理程序。

<input
  type="checkbox"
  onClick={() => {
    this.handleCheckboxClick(todo.id)
  }}
/>

我們使用了一個稍微奇怪的語法温峭,因為我們希望將所選todo的id傳遞給處理函數(shù)猛铅。
處理程序應(yīng)該添加到render函數(shù)之上。

handleCheckboxClick(id) {
    let newTodos = [...this.state.todos];
    newTodos[newTodos.findIndex(todo => todo.id === id)].done = true;
    this.setState({
      todos: newTodos
    });
  }

這是React中修改數(shù)組狀態(tài)的多種方法之一凤藏。首先復(fù)制當(dāng)前todos列表奸忽,然后將選擇的todo(通過其id標(biāo)識)標(biāo)記為done并更新狀態(tài)堕伪。

現(xiàn)在,當(dāng)你選中復(fù)選框時栗菜,todo就會從頁面中消失欠雌,因為我們將過濾掉標(biāo)記為done的任何todo 項。

添加 Todos

在現(xiàn)實生活中疙筹,人們可能有比洗碗和打掃房間更多的任務(wù)要做富俄,所以讓我們允許用戶添加他們自己的待辦事項。

首先而咆,向render方法添加一個輸入表單霍比。

render() {
    return (
      <div
        style={{ padding: "30px 0" }}
        className="ui text container center aligned"
      >
        <h2>My Todos</h2>
        <div className="ui grid">
          <div className="row centered">
            <div className="column twelve wide">
              <form className="ui form" onSubmit={this.handleAddTodoClick}>
                <div className="inline fields">
                  <div className="twelve wide field">
                    <input
                      type="text"
                      value={this.state.newTodo}
                      onChange={this.hanldeInputChange}
                    />
                  </div>
                  <button className="ui button primary" type="submit">
                    Add todo
                  </button>
                </div>
              </form>
            </div>
          </div>
          <div className="row centered">
            <div className="column twelve wide">
              <div className="grouped fields">
                {this.state.todos
                  .filter(todo => !todo.done)
                  .map(todo => (
                    <div key={todo.id} className="field">
                      <div className="ui checkbox">
                        <input
                          type="checkbox"
                          onClick={() => {
                            this.handleCheckboxClick(todo.id);
                          }}
                        />
                        <label>{todo.title}</label>
                      </div>
                    </div>
                  ))}
              </div>
            </div>
          </div>
        </div>
      </div>
    );
  }

然后讓我們實現(xiàn)所有這些處理函數(shù)。

更新初始狀態(tài)以跟蹤新的todo值暴备,并清除那些默認的todo桂塞。

state = {
  todos: [],
  newTodo: "",
}

實現(xiàn)handleInputChange函數(shù),該函數(shù)將跟蹤用戶輸入的內(nèi)容馍驯。

hanldeInputChange = e => {
  this.setState({
    newTodo: e.target.value,
  })
}

接下來,我們實現(xiàn)handleAddTodoClick玛痊,當(dāng)用戶單擊enter或單擊按鈕來添加他們的新todo項時汰瘫,將調(diào)用handleAddTodoClick

handleAddTodoClick = e => {
  e.preventDefault()
  const newTodo = {
    id: this.state.todos.length + 1,
    title: this.state.newTodo,
    done: false,
  }
  const todos = [...this.state.todos]
  todos.push(newTodo)
  this.setState({
    todos: todos,
    newTodo: "",
  })
}

你的整個App.js應(yīng)該是這樣的:

import React from "react"

class App extends React.Component {
  state = {
    todos: [],
    newTodo: "",
  }

  handleCheckboxClick(id) {
    let newTodos = [...this.state.todos]
    newTodos[newTodos.findIndex(todo => todo.id === id)].done = true
    this.setState({
      todos: newTodos,
    })
  }

  handleAddTodoClick = e => {
    e.preventDefault()
    const newTodo = {
      id: this.state.todos.length + 1,
      title: this.state.newTodo,
      done: false,
    }
    const todos = [...this.state.todos]
    todos.push(newTodo)
    this.setState({
      todos: todos,
      newTodo: "",
    })
  }

  hanldeInputChange = e => {
    this.setState({
      newTodo: e.target.value,
    })
  }

  render() {
    return (
      <div
        style={{ padding: "30px 0" }}
        className="ui text container center aligned"
      >
        <h2>My Todos</h2>
        <div className="ui grid">
          <div className="row centered">
            <div className="column twelve wide">
              <form className="ui form" onSubmit={this.handleAddTodoClick}>
                <div className="inline fields">
                  <div className="twelve wide field">
                    <input
                      type="text"
                      value={this.state.newTodo}
                      onChange={this.hanldeInputChange}
                    />
                  </div>
                  <button className="ui button primary" type="submit">
                    Add todo
                  </button>
                </div>
              </form>
            </div>
          </div>
          <div className="row centered">
            <div className="column twelve wide">
              <div className="grouped fields">
                {this.state.todos
                  .filter(todo => !todo.done)
                  .map(todo => (
                    <div key={todo.id} className="field">
                      <div className="ui checkbox">
                        <input
                          type="checkbox"
                          onClick={() => {
                            this.handleCheckboxClick(todo.id)
                          }}
                        />
                        <label>{todo.title}</label>
                      </div>
                    </div>
                  ))}
              </div>
            </div>
          </div>
        </div>
      </div>
    )
  }
}

export default App

現(xiàn)在擂煞,你應(yīng)該能夠添加新的待辦事項混弥,并勾選它們。唯一的問題是对省,當(dāng)你刷新頁面時蝗拿,你會丟失所有珍貴的待辦事項。現(xiàn)在是時候使用Blockstack來保存我們的todo了蒿涎。

我們加上 Blockstack

現(xiàn)在哀托,我們將使用Blockstack添加用戶身份驗證和存儲。先停止App劳秋,安裝 npm install blockstack仓手,然后我們可以重新啟動應(yīng)用程序npm start

身份認證

在blockstack 的App.js的類聲明的上方添加以下行:

import { UserSession, AppConfig } from "blockstack";

const appConfig = new AppConfig(["store_write"]);
const userSession = new UserSession({ appConfig: appConfig });

class App extends React.Component {
    ...
}

這一行const appConfig = new AppConfig(["store_write"]);用于設(shè)置Blockstack App 的配置玻淑。 你要向用戶請求所需的權(quán)限嗽冒。在這個例子中,我們請求store_write權(quán)限补履,它允許我們將數(shù)據(jù)存儲在用戶的私有存儲中添坊。
如果我們想構(gòu)建社交類的App,我們需要publish_data權(quán)限箫锤,它允許某些用戶數(shù)據(jù)對其他用戶可見贬蛙。
const userSession = new UserSession({ appConfig: appConfig });
建立用戶session雨女,允許我們處理身份驗證。

在頁面頂部添加一個登錄按鈕速客。

<div style={{ padding: "30px 0" }} className="ui text container center aligned">
  <button className="ui button positive" onClick={this.handleSignIn}>
    Sign in with blockstack
  </button>
  <h2>My Todos</h2>
  ...
</div>

And implement our handler function this.handleSignIn like this:

并實現(xiàn)函數(shù) this.handleSignIn

handleSignIn = () => {
  userSession.redirectToSignIn()
}

實現(xiàn)登錄只需要一行代碼戚篙,頁面現(xiàn)在應(yīng)該是這樣的:

登錄頁面

我們點擊那個按鈕,看看會發(fā)生什么!
我們被帶到blockstack瀏覽器登錄溺职,但看起來有一個問題

提示錯誤

提示"Failed to fetch information about the app requesting authentication. Please contact the app maintainer to resolve the issue." 不知道在說什么岔擂,但控制臺顯示了一些更有用的東西。

Access to fetch at 'http://localhost:3000/manifest.json' 
from origin 'https://browser.blockstack.org' has been blocked by CORS policy: 
No 'Access-Control-Allow-Origin' header is present on the requested resource.
If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.

這實際上這是一個很常見的bug浪耘。

解決 CORS 問題

這個問題是Blockstack瀏覽器試圖從你的網(wǎng)站訪問一個名為manifest.json的文件乱灵,其中包含App的一些信息。由于CORS七冲,在默認情況下痛倚,網(wǎng)站在不同的域不能向其他網(wǎng)站發(fā)出請求 。這樣做是出于安全澜躺。所以我們的網(wǎng)站現(xiàn)在拒絕了Blockstack瀏覽器對我們manifest.json的請求蝉稳。實際上希望Blockstack能夠訪問該文件。

To do that, we'll need to modify our webpack config. Since we used create-react-app , the webpack config is hidden. To modify it, we use the command npm run eject. You will probably get a warning about having untracked files and uncommitted changes. So commit all your changes to git first.

為此掘鄙,我們需要修改webpack配置耘戚。因為我們使用了create- response -app,webpack配置是隱藏的操漠。要修改它收津,我們使用命令npm run eject。就會得到一個關(guān)于git的警告浊伙。因此撞秋,首先將所有更改提交到git。

git add -A
git commit -m "did things"
npm run eject
git提交

You'll see two new folders in your directory called scripts and config. Go to config/webpackDevServer.config.js and add the following line on top of the module exports function.

在目錄中看到兩個新文件夾scriptsconfig嚣鄙,在 config / webpackDevServer.config 添加以下行吻贿。

module.exports = function(proxy, allowedHost) {
  return {
    headers: {
      "Access-Control-Allow-Origin": "*"
    },

    // WebpackDevServer 2.4.3 introduced a security fix that prevents remote
    // websites from potentially accessing local content through DNS rebinding:

    ...

  }
}

使用npm start重新啟動并重新登錄。

進入public/manifest.json哑子,在這里修改 App 的名稱廓八。

{
  "short_name": "Todo List",
  "name": "Secure Todo List",
  "icons": [
    {
      "src": "favicon.ico",
      "sizes": "64x64 32x32 24x24 16x16",
      "type": "image/x-icon"
    }
  ],
  "start_url": ".",
  "display": "standalone",
  "theme_color": "#000000",
  "background_color": "#ffffff"
}

Authentication Continued 繼續(xù)驗證

現(xiàn)在,根據(jù)用戶是否登錄來修改頁面赵抢。退出的用戶不應(yīng)該看到他們的todo列表剧蹂,而登錄后的用戶不需要看到login按鈕。

To make this a bit cleaner, we're going to separate those two things into different components. We'll have a TodoList component which shows the Todo List and a Login component which shows the login page.

為了是 App更簡潔烦却,我們把這兩個東西分成不同的部分宠叼。我們將有一個顯示Todo列表的TodoList組件和一個顯示登錄頁面的Login組件。

Copy the contents of App.js into a new file called TodoList.js and modify it as follows.

復(fù)制 App.js的內(nèi)容的到新的頁面 TodoList.js, 修改如下:

import React from "react"

class TodoList extends React.Component {
  state = {
    todos: [],
    newTodo: "",
  }

  handleCheckboxClick(id) {
    let newTodos = [...this.state.todos]
    newTodos[newTodos.findIndex(todo => todo.id === id)].done = true
    this.setState({
      todos: newTodos,
    })
  }

  handleAddTodoClick = e => {
    e.preventDefault()
    const newTodo = {
      id: this.state.todos.length + 1,
      title: this.state.newTodo,
      done: false,
    }
    const todos = [...this.state.todos]
    todos.push(newTodo)
    this.setState({
      todos: todos,
      newTodo: "",
    })
  }

  hanldeInputChange = e => {
    this.setState({
      newTodo: e.target.value,
    })
  }

  render() {
    return (
      <div
        style={{ padding: "30px 0" }}
        className="ui text container center aligned"
      >
        <h2>My Todos</h2>
        <div className="ui grid">
          <div className="row centered">
            <div className="column twelve wide">
              <form className="ui form" onSubmit={this.handleAddTodoClick}>
                <div className="inline fields">
                  <div className="twelve wide field">
                    <input
                      type="text"
                      value={this.state.newTodo}
                      onChange={this.hanldeInputChange}
                    />
                  </div>
                  <button className="ui button primary" type="submit">
                    Add todo
                  </button>
                </div>
              </form>
            </div>
          </div>
          <div className="row centered">
            <div className="column twelve wide">
              <div className="grouped fields">
                {this.state.todos
                  .filter(todo => !todo.done)
                  .map(todo => (
                    <div key={todo.id} className="field">
                      <div className="ui checkbox">
                        <input
                          type="checkbox"
                          onClick={() => {
                            this.handleCheckboxClick(todo.id)
                          }}
                        />
                        <label>{todo.title}</label>
                      </div>
                    </div>
                  ))}
              </div>
            </div>
          </div>
        </div>
      </div>
    )
  }
}

export default TodoList

寫一個 Login.js 組件冒冬,代碼如下

import React from "react"

class Login extends React.Component {
  handleSignIn = () => {
    this.props.userSession.redirectToSignIn()
  }

  render() {
    return (
      <div
        style={{ padding: "30px 0" }}
        className="ui text container center aligned"
      >
        <h1>Decentralized Todo List</h1>
        <p>This is the most secure todo list on the market.</p>

        <button className="ui button positive" onClick={this.handleSignIn}>
          Sign in with blockstack
        </button>
      </div>
    )
  }
}

export default Login

我們將userSession作為props伸蚯,這個對象包含用戶身份驗證相關(guān)的函數(shù)。

最后當(dāng)用戶注銷時简烤,App.js 顯示Login組件剂邮,當(dāng)用戶登錄后顯示TodoList組件。

import React from "react"
import { UserSession, AppConfig } from "blockstack"
import Login from "./Login"
import TodoList from "./TodoList"
const appConfig = new AppConfig(["store_write"])
const userSession = new UserSession({ appConfig: appConfig })

class App extends React.Component {
  render() {
    return (
      <div>
        {userSession.isUserSignedIn() ? (
          <TodoList userSession={userSession} />
        ) : (
          <Login userSession={userSession} />
        )}
      </div>
    )
  }
}

export default App

We use the function userSession.isUserSignedIn() to find out whether there is a logged in user or not.

Now you should see the login page by default. When you click the button, you are redirected to Blockstack, then once you select your id you are redirected to your app, then...it still shows you the login page. What's up with that?

Turns out we're actually in an intermediary login stage. By this point, Blockstack has given the app a token with all of the user information. We need to add one more function call to extract information from that toke and finish the sign in.

Add these lines above the render() function in your App component.

componentWillMount() {
    if (userSession.isSignInPending()) {
      userSession
        .handlePendingSignIn()
        .then(() => {
          window.location = window.location.origin;
        })
        .catch(err => console.log(err));
    }
  }

This extracts the user information from the token, and completes the sign in, then refreshes the page.

Here is a chart that explains the whole Blockstack authentication process.

With this in place, try logging in again and you should be redirected to the todo list.

Lastly, let's add a sign out button to the todo list page. Go to TodoList.js and add a button to the top of the page in the render function.

 <div
        style={{ padding: "30px 0" }}
        className="ui text container center aligned"
      >
        <button className="ui button negative" onClick={this.handleSignout}>
          Sign out
        </button>

        <h2>My Todos</h2>
        <div className="ui grid">

            ...

     </div>
</div>

Add the handleSignout function somewhere above the render function.

handleSignout = () => {
  this.props.userSession.signUserOut(window.location.origin)
}

Now you can login and logout of the app with Blockstack.

Storing The Todos

Now that the user can login to our app, we can store their data with Blockstack.

We'll be using two core functions of the blockstack.js library: putFile and getFile.

They do exactly what they sound like. putFile allows you to store files, and getFile allows you to retrieve files. You can store any type of file, and they can be encrypted if you want.

In our case, we'll be storing our todos in JSON format because it makes them easy to handle.

Go to TodoList.js and modify the handleAddTodoClick function as follows:

handleAddTodoClick = e => {
  e.preventDefault()
  const newTodo = {
    id: this.state.todos.length + 1,
    title: this.state.newTodo,
    done: false,
  }
  const todos = [...this.state.todos]
  todos.push(newTodo)
  const options = { encrypt: true }
  this.props.userSession
    .putFile("todos.json", JSON.stringify(todos), options)
    .then(() => {
      this.setState({
        todos,
        newTodo: "",
      })
    })
}

This stores all the user's todos in a file called todos.json

Modify handleCheckboxClick so that when we mark todos as done, this is also updated in the user storage.

handleCheckboxClick(id) {
    let newTodos = [...this.state.todos];
    newTodos[newTodos.findIndex(todo => todo.id === id)].done = true;
    const options = { encrypt: true };
    this.props.userSession
      .putFile("todos.json", JSON.stringify(newTodos), options)
      .then(() => {
        this.setState({
          todos: newTodos
        });
      });
  }

Try making some todos now and you should see something like this in your console, indicating that the files were stored.

If you refresh the page you won't see anything, because we still need to retrieve the todos.

Add a new function to your class called fetchData which will get the todo list from user storage.

async fetchData() {
    const options = { decrypt: true };
    const file = await this.props.userSession.getFile("todos.json", options);
    let todos = JSON.parse(file || "[]");
    this.setState({
      todos
    });
  }

We will call this function in our componentDidMount

componentDidMount() {
    this.fetchData();
}

Now you can add a todo item, refresh your page, and it will still be there!

Adding User Profile Data

Right now our app doesn't feel very personal, but we can use Blockstack to get information like the user's name to customize their experience.

Add a new field to the state to store the user object.

state = {
  newTodo: "",
  todos: [],
  user: null,
}

Then modify the fetchData function to update the state with user info.

async fetchData() {
    const options = { decrypt: true };
    const file = await this.props.userSession.getFile("todos.json", options);
    let todos = JSON.parse(file || "[]");
    this.setState({
      todos,
      user: new Person(this.props.userSession.loadUserData().profile)
    });
  }

And add an import statement at the top of your file.

import { Person } from "blockstack"

The Person object puts the user data in an easily accessible format.

Modify the render function to display some user information. We'll be showing their name and profile image.

render() {
    const { user } = this.state;

    return (
      <div
        style={{ padding: "30px 0" }}
        className="ui text container center aligned"
      >
        <button className="ui button negative" onClick={this.handleSignout}>
          Sign out
        </button>
        <h1>{user && user.name()}</h1>
        <img
          className="ui centered medium rounded image"
          src={user && user.avatarUrl()}
          alt="user profile image"
        />
        <h2>My Todos</h2>

        ...

Now the app should feature the user's name and profile image.

Our app looks good to go, now let's deploy it for the rest of the world to see.

Deploying To Netlify

There are many ways to deploy your React app, but Netlify is one of the best. It allows you to easily setup continuous deployment.

First let's make a new repository on github.

Add and commit all of your files.

git add -A
git commit -m "made everything"

Then follow the commands to push an existing repository. For me that would be:

git remote add origin https://github.com/dkb868/secure-todo-list.git
git push -u origin master

Now you should have a beautiful new repo up on github.

Make an account on Netlify, then in your dashboard, select "New site from Git".

Select Github, and search for your repo.

Use the following build settings, then click Deploy Site

Give it a few minutes, then you should have your site up at something.netlify.com. You can modify this name if you want, or add a custom domain.

If we go to our newly launched app, we'll see a familiar error.

We know this is a CORS error, and we fixed it in our development environment, so now we need to fix it in production.

With Netlify, this is as simple as adding a netlify.toml file in your root project directory.

[[headers]]
  for = "/*"
  [headers.values]
  Access-Control-Allow-Origin = "*"

Add that file and push it to GitHub. Once you have continuous deploy enabled, it will be deployed automatically in a few minutes.

Now everything should be working great.

Conclusion

If you made it this far, congrats for finishing the app!

If you got lost at some point, you can check out the github repo or the demo website for reference.

Demo Website: https://blockstack-todo-list.netlify.com/

Github Repo: https://github.com/dkb868/secure-todo-list

This is my first coding tutorial, so if you have any feedback on things I can improve, please let me know.


參考:

http://www.reibang.com/p/078c1dae4397

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末横侦,一起剝皮案震驚了整個濱河市挥萌,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌枉侧,老刑警劉巖引瀑,帶你破解...
    沈念sama閱讀 221,548評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異榨馁,居然都是意外死亡憨栽,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,497評論 3 399
  • 文/潘曉璐 我一進店門翼虫,熙熙樓的掌柜王于貴愁眉苦臉地迎上來屑柔,“玉大人,你說我怎么就攤上這事珍剑【庵” “怎么了?”我有些...
    開封第一講書人閱讀 167,990評論 0 360
  • 文/不壞的土叔 我叫張陵次慢,是天一觀的道長。 經(jīng)常有香客問我翔曲,道長迫像,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,618評論 1 296
  • 正文 為了忘掉前任瞳遍,我火速辦了婚禮闻妓,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘掠械。我一直安慰自己由缆,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 68,618評論 6 397
  • 文/花漫 我一把揭開白布猾蒂。 她就那樣靜靜地躺著均唉,像睡著了一般。 火紅的嫁衣襯著肌膚如雪肚菠。 梳的紋絲不亂的頭發(fā)上舔箭,一...
    開封第一講書人閱讀 52,246評論 1 308
  • 那天,我揣著相機與錄音,去河邊找鬼层扶。 笑死箫章,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的镜会。 我是一名探鬼主播檬寂,決...
    沈念sama閱讀 40,819評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼戳表!你這毒婦竟也來了桶至?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,725評論 0 276
  • 序言:老撾萬榮一對情侶失蹤扒袖,失蹤者是張志新(化名)和其女友劉穎塞茅,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體季率,經(jīng)...
    沈念sama閱讀 46,268評論 1 320
  • 正文 獨居荒郊野嶺守林人離奇死亡野瘦,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,356評論 3 340
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了飒泻。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片鞭光。...
    茶點故事閱讀 40,488評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖泞遗,靈堂內(nèi)的尸體忽然破棺而出惰许,到底是詐尸還是另有隱情,我是刑警寧澤史辙,帶...
    沈念sama閱讀 36,181評論 5 350
  • 正文 年R本政府宣布汹买,位于F島的核電站,受9級特大地震影響聊倔,放射性物質(zhì)發(fā)生泄漏晦毙。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,862評論 3 333
  • 文/蒙蒙 一耙蔑、第九天 我趴在偏房一處隱蔽的房頂上張望见妒。 院中可真熱鬧,春花似錦甸陌、人聲如沸须揣。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,331評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽耻卡。三九已至,卻和暖如春牲尺,著一層夾襖步出監(jiān)牢的瞬間劲赠,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,445評論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留凛澎,地道東北人霹肝。 一個月前我還...
    沈念sama閱讀 48,897評論 3 376
  • 正文 我出身青樓,卻偏偏與公主長得像塑煎,于是被迫代替她去往敵國和親沫换。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,500評論 2 359

推薦閱讀更多精彩內(nèi)容