ZeroNet Blogs

Static ZeroNet blogs mirror

DDNS

我注册了免费的.cf域名,然后用CloudXNS管理。通过CloudXNS的API可以路由器上自动更新IP地址。 注册方面不细说,只说路由器的配置。我用的是修改版的Padavan固件,里面自带有CloudXNS功能。但是我觉得自带的这个脚本并不好,它是在后台运行脚本,然后一定时间通过互联网的服务检查IP地址。其实不用这么麻烦,路由器联网或断网都会执行一个脚本,在这个脚本里面更新是最好的。 修改过的代码:https://gist.github.com/ysc3839/5ebf6483b10489964cd0ea2477544ecd

ZNC

ZNC是一个用C++写成的IRC Bouncer。IRC的服务器是只起转发作用的,不会存储历史消息,所以你下线的时候没法看到消息。而IRC Bouncer就可以保存下你的消息。 安装:用opkg安装很简单,直接执行opkg install znc。一般会同时装上WebAdmin模块opkg install znc-mod-webadmin,这样可以用网页进行一些设置。 我这里装好后执行znc会提示Floating point exception,设置一下lib目录可以解决问题export LD_LIBRARY_PATH=/opt/lib。需要注意的是,设置之后执行路由器本身的程序可能会出现问题,这时需要unset LD_LIBRARY_PATH。 配置就不细说了,网上有教程。安装znc的时候会设置自动启动,在/opt/etc/init.d/S99znc,你需要按照前面说的改一下才能正常启动。另外znc不允许root用户运行,可以使用znc自带的droproot插件,但是在Web界面设置的话是无法启用droproot的,所以我把开启droproot的代码一并写到了S99znc里面。https://gist.github.com/ysc3839/75eb3857f6005cb070ce56c1577eecc2

HTTPS

虽然路由器是在国内的,但是不加密也有一定风险。我使用Let's Encrypt的证书。我使用的是acme.sh申请。用的是DNS方式,不需要网页服务器验证,同时这个脚本可以通过CloudXNS的API自动设置验证的参数(链接)。 拿到证书后,还要合并成一个.pem文件才能用在znc上,依次执行(把yourdomain.com换成你的域名)

cat yourdomain.com.key > pem.pem
cat yourdomain.com.cer >> pem.pem
cat ca.cer >> pem.pem
cat fullchain.cer >> pem.pem

然后用pem.pem替换znc.pem即可。 另外,znc设置SSL之后通过http方式访问会被RESET,必须通过https访问,也许是个bug。

[翻译]ZeroChat 教程 (新)

- Posted in YSC's blog by with comments

原文链接: ZeroChat tutorial (new)

In this tutorial we going to build a P2P, decentralized, server and backend-less chat site in less then 100 lines of code. (This is a simplified and pure-javascript rework of the original tutorial.)


Creating new site

  • Click on ⋮ > “Create new, empty site” menu item on the site ZeroHello.
  • You will be redirected to a completely new site that is only modifiable by you!
  • Drag the top-right “0” button to left to show the sidebar
  • At the bottom change the Site title to “My ZeroChat tutorial”, then press “Save site settings”.

The chat site’s HTML code

  • Open “data/[yoursiteaddress]/index.html” file in you favorite editor.
  • Change the <body> tag’s content to:
<body>

<a href="#Select+user" id="select_user" onclick='return page.selectUser()'>Select user</a>:
<input type="text" id="message">
<input type="button" id="send" value="Send!" onclick="return page.sendMessage()"/>
<ul id="messages">
 <li>Welcome to ZeroChat!</li>
</ul>
<script type="text/javascript" src="js/ZeroFrame.js"></script>

</body>

The js/ZeroFrame.js file (automatically bundled if you create your site using ZeroHello) contains the ZeroFrame class that allows us to communicate with the websocket based ZeroFrame API.

First ZeroFrame API call

  • Initialize our application in a <script> block, after the ZeroFrame.js include:
<script type="text/javascript" src="js/ZeroFrame.js"></script>

<script>
class ZeroChat extends ZeroFrame {
    addMessage (username, message) {
        var message_escaped = message.replace(/</g, "&lt;").replace(/>/g, "&gt;")  // Escape html tags in the message
        this.messages.innerHTML += "<li><b>" + username + "</b>: " + message_escaped + "</li>"
    }

    onOpenWebsocket () {
        this.messages = document.getElementById("messages")
        this.addMessage("System", "Ready to call ZeroFrame API!")
    }
}

page = new ZeroChat()
</script>
  • If we reload the page we should see a “Ready to call ZeroFrame API!” message.
  • To make the “Select User” button display the user select dialog add the following function to the ZeroChat class:
    selectUser () {
        this.cmd("certSelect", {accepted_domains: ["zeroid.bit"]})
        return false
    }

Display user’s current ZeroID account

When something changed that is affects the site (new content arrived, user changed, etc.) a websocket event will be pushed to your browser. (the format is same as you query siteInfo command) - To handle this event add this to our class:

    onRequest (cmd, message) {
        if (cmd == "setSiteInfo") {
            if (message.params.cert_user_id)
                document.getElementById("select_user").innerHTML = message.params.cert_user_id
            else
                document.getElementById("select_user").innerHTML = "Select user"
            this.site_info = message.params  // Save site info data to allow access it later
        }
    }

This code will real-time update the currently selected user name.

  • To also display the currently selected username when the page loads add this to “onOpenWebsocket” function:
        this.cmd("siteInfo", {}, (site_info) => {
            if (site_info.cert_user_id)
                document.getElementById("select_user").innerText = site_info.cert_user_id
            this.site_info = site_info
        })

Now our application should always display the currently selected user correctly. (Even if its modified from other browser window)

Setting user content permissions

To allow other users to post on our site we have to define the rules of the third-party content.

Create a data/users/content.json file in your site’s directory:

{
  "files": {},
  "ignore": ".*",
  "modified": 0.0,
  "signs": {},
  "user_contents": {
    "cert_signers": {
      "zeroid.bit": [ "1iD5ZQJMNXu43w1qLB8sfdHVKppVMduGz" ]
    },
    "permission_rules": {
      ".*": {
        "files_allowed": "data.json",
        "max_size": 10000
      },
      "bitmsg/.*@zeroid.bit": { "max_size": 15000 }
    },
    "permissions": {
      "bad@zeroid.bit": false,
      "nofish@zeroid.bit": { "max_size": 100000 }
    }
  }
}
  • "ignore": ".*": The files in this directory will be signed by the users and not by the site’s owner.
  • cert_signers: We accept *@zeroid.bit users and they have to come with a cert that is has to signed by 1iD5ZQJMNXu43w1qLB8sfdHVKppVMduGz address.
  • permission_rules: We give 10kbytes of space to every user (15kb if registered using bitmessage)
  • permissions: Per-user permissions: ban “bad@zeroid.bit” user and allow 100k storage to “nofish@zeroid.bit” user. ( it's me :) )

After we saved this file we also have to modify our site’s root content.json to ignore everything in this directory on signing and load the file containing the rules:

  ...
    "ignore": "data/.*",
    "includes": {
        "data/users/content.json": {
            "signers": [],
            "signers_required": 1
        }
    },
  ...

Note: You can give moderation permissions to other users by adding addresses to “signers” list.

  • Sign the root content.json modifications by pressing the “Sign” button on the sidebar.
  • Then keep the sidebar open and change “content.json” to “data/users/content.json” and press the “Sign” button again!

Adding messages to our json file

When hitting the Send button we going to add the message to our user’s data.json file, sign it, then publish it to other users. - Create a new function in ZeroChat class:

    sendMessage () {
        if (!this.site_info.cert_user_id) {  // No account selected, display error
            this.cmd("wrapperNotification", ["info", "Please, select your account."])
            return false
        }

        // This is our data file path
        var inner_path = "data/users/" + this.site_info.auth_address + "/data.json"

        // Load our current messages
        this.cmd("fileGet", {"inner_path": inner_path, "required": false}, (data) => {
            if (data)  // Parse current data file
                data = JSON.parse(data)
            else  // Not exists yet, use default data
                data = { "message": [] }

            // Add the new message to data
            data.message.push({
                "body": document.getElementById("message").value,
                "date_added": Date.now()
            })

            // Encode data array to utf8 json text
            var json_raw = unescape(encodeURIComponent(JSON.stringify(data, undefined, '\t')))

            // Write file to disk
            this.cmd("fileWrite", [inner_path, btoa(json_raw)], (res) => {
                if (res == "ok") {
                    // Reset the message input
                    document.getElementById("message").value = ""
                    // Publish the file to other users
                    this.cmd("sitePublish", {"inner_path": inner_path})
                } else {
                    this.cmd("wrapperNotification", ["error", "File write error: #{res}"])
                }
            })
        })

        return false
    }
  • After this modification type something to the message input and press the Send button! You should see the message in the data/users/[your auth address]/data.json file.

    (Don’t worry if you see “Publish failed” message: it is normal, since we don’t have any other users on our site yet)

Creating database

Now we can save and publish our messages to other users, let’s display it in our application! The best way to do this is map all data.json files to an SQL database.

The ZeroNet automatically do this for you, all you need is a dbschema.json file in your site’s directory that describe your table structure:

{
    "db_name": "ZeroChat",
    "db_file": "data/zerochat.db",
    "version": 2,
    "maps": {
        "users/.+/data.json": {
            "to_table": [ "message" ]
        },
        "users/.+/content.json": {
            "to_json_table": [ "cert_user_id" ],
            "file_name": "data.json"
        }
    },
    "tables": {
        "json": {
            "cols": [
                ["json_id", "INTEGER PRIMARY KEY AUTOINCREMENT"],
                ["directory", "TEXT"],
                ["file_name", "TEXT"],
                ["cert_user_id", "TEXT"]
            ],
            "indexes": ["CREATE UNIQUE INDEX path ON json(directory, file_name)"],
            "schema_changed": 10
        },
        "message": {
            "cols": [
                ["body", "TEXT"],
                ["date_added", "INTEGER"],
                ["json_id", "INTEGER REFERENCES json (json_id)"]
            ],
            "indexes": ["CREATE UNIQUE INDEX message_key ON message(json_id, date_added)"],
            "schema_changed": 10
        }
    }
}
  • "db_name": "ZeroChat": Used only for debugging
  • "db_file": "data/zerochat.db": The SQLite database file will be stored here
  • "version": 2: Define the json table structure, version 2 is better suited to ZeroID based sites. More info in the reference docs.
  • "maps": {: Describes the json files -> table conversion
  • "users/.+/data.json": { "to_table": [ "message" ] }: Put the data from every user's data.json file message node to message table.
  • "users/.+/content.json": { "to_json_table": [ "cert_user_id" ], "file_name": "data.json"}: For easy SQL Join store the username in the data.json file's json table entry.
  • "tables": {: Describe the tables and indexing structure. A json table entry will be created automatically for every json file.
  • ["json_id", "INTEGER REFERENCES json (json_id)"]: Every table should contain a json_id column, it defines the source file path.
  • "schema_changed": 10: Increment this when you change the table structure, so the peers can drop the table and re-create it from the json files.

Tip: For the best performance always create an index for json_id column in your tables, because when new file arrives the data will be updated based on this column.

  • Press the Reload, then the Rebuild button on the sidebar to generate the database.

If you did everything well, then the data/zerochat.db SQLite file will be created. To browse these files I recommend using SQLiteStudio (it’s free and opensource)

Displaying messages

The messages are loaded to the SQL database, so we can easily query them using the dbQuery command:

    loadMessages () {
        this.cmd("dbQuery", ["SELECT * FROM message LEFT JOIN json USING (json_id) ORDER BY date_added DESC"], (messages) => {
            document.getElementById("messages").innerHTML = ""  // Always start with empty messages
            for (var i=0; i < messages.length; i++) {
                this.addMessage(messages[i].cert_user_id, messages[i].body)
            }
        })
    }

Add this.loadMessages() to onOpenWebsocket function, then reload the page and you should see the messages you typed in.

To make it “real time” and display new messages immediately as they come in you also have to add the this.loadMessages() to the onRequest function:

    onRequest (cmd, message) {
        if (cmd == "setSiteInfo") {
            if (message.params.cert_user_id)
                document.getElementById("select_user").innerHTML = message.params.cert_user_id
            else
                document.getElementById("select_user").innerHTML = "Select user"
            this.site_info = message.params  // Save site info data to allow access it later

            // Reload messages if new file arrives
            if (message.params.event[0] == "file_done")
                this.loadMessages()
        }
    }

Also reload the data when we submit a new message:

    sendMessage () {
        ...
            // Write file to disk
            this.cmd("fileWrite", [inner_path, btoa(json_raw)], (res) => {
                this.loadMessages()
                ...
        ...

That’s it! Now the messages are updated in real-time! You can try it by opening an another browser window and enter a messages there.

Final touches

  • Send messages by pressing enter
<input type="text" id="message" onkeypress="if (event.keyCode == 13) page.sendMessage()">
  • And add some CSS style to make it look better
<style>
* { font-family: monospace; line-height: 1.5em; font-size: 13px; vertical-align: middle; }
body { background-color: white; }
input#message { padding: 5px; width: 50%;  }
input#send { height: 34px; margin-left: -2px; }
ul { padding: 0px; }
li { list-style-type: none; border-top: 1px solid #eee; padding: 5px 0px; }
li:nth-child(odd) { background-color: #F9FAFD; }
li b { color: #3F51B5; }
</style>

Congratulations! You have completed the ZeroChat tutorial! :)

Some idea for improvements: - Display message time - Move source code to separate .js file - Limit displayed messages / add paging - Markdown formatting - Username mention

Tip: The .js files are cached. To avoid it you need to keep open the Dev Console (F12) with "Disable cache" checked.

Tip: Don't forget to re-sign and publish your site if you do any modification on the source code.

Tip: Your site's private key is in data/users.json file (search for your site's address, then there will be a privatekey entry), so it's a good idea to backup it!

Tip: ZeroNet has a built-in .js and .css merging feature for developers. To enable it add debug to zeronet.conf file's [global] section, then it will response every file's content in the directory if you request all.js or all.css

本博客开源代码

- Posted in YSC's blog by with comments

本博客原始代码基于 https://github.com/zeronetscript/ZeroBlog (也就是大家说的Enhanced ZeroBlog) 进行修改 源代码开放于 https://github.com/ysc3839/ZeroBlog 欢迎贡献代码!

大概是翻译成了中文,修改了一下字体啥的……具体请看commits 与诸多修改版不同的是,我是直接修改CoffeeScript的,而不是修改js代码

测试站点:(应该可以直接克隆)https://www.zerogate.tk/19qHLGN2KEoU6CJipGbSK1hxDvUNKBWHGc/

[翻译]ZeroNet 站点开发教程 #2

- Posted in YSC's blog by with comments

原文链接: ZeroNet site development tutorial #2

第一部分 我们使用了一些简单的 ZeroFrame API 调用创建了一个 ZeroNet 站点。

We going to extend it to accept, store and query messages using a SQLite database and use ZeroID to identify user names.

The final page and source code is available at https://www.zerogate.tk/1AvF5TpcaamRNtqvN1cnDEWzNmUtD47Npg


添加 ZeroID 用户选择

Add a new link to index.html that will allow us to select the ZeroID identity we want to use:

<html>
<body>
<a href="#Select+user" id="select_user" onclick='return Page.selectUser()'>Select user</a>:
<input type="text" id="message"><input type="button" id="send" value="Send!"/>
<ul id="messages">
 <li>Welcome to ZeroChat!</li>
</ul>
<script type="text/javascript" src="js/all.js" async></script>
</body>
</html>

To make it work add a function to js/ZeroChat.coffee that displays the certificate selection dialog to user:

    selectUser: =>
        Page.cmd "certSelect", [["zeroid.bit"]]
        return false

As parameter you should include the accepted certificate provider names. (zeroid.bit here)

If you hit a refresh and click on "Select user", then you should see the dialog, but nothing changes when you select your zeroid certificate.

显示用户当前的 ZeroID 账户

When something is changed that affects the site (new content arrived, user changed, etc.) a websocket event will be pushed to your browser. (the format is same as you query setSiteInfo command)

To handle this event add this function:

    route: (cmd, message) ->
        if cmd == "setSiteInfo"
            if message.params.cert_user_id
                document.getElementById("select_user").innerHTML = message.params.cert_user_id
            else
                document.getElementById("select_user").innerHTML = "Select user"
            @site_info = message.params  # Save site info data to allow access it later

This code will real-time update the user's currently selected user name.

To also update the user name on page load modify the onOpenWebsocket function:

    onOpenWebsocket: (e) =>
        @cmd "serverInfo", {}, (server_info) =>
            @addLine "serverInfo response: <pre>" + JSON.stringify(server_info,null,2) + "</pre>"
        @cmd "siteInfo", {}, (site_info) =>
            @addLine "siteInfo response: <pre>" + JSON.stringify(site_info,null,2) + "</pre>"
            # Update currently selected username
            if site_info.cert_user_id
                document.getElementById("select_user").innerHTML = site_info.cert_user_id
            @site_info = site_info  # Save site info data to allow access it later

设置用户内容权限 (content permissions)

To allow users to post on our site we have to define the rules of the third-party content.

Create a data/users directory and create a data/users/content.json file in it:

{
  "files": {},
  "ignore": ".*",
  "modified": 0.0,
  "signs": {},
  "user_contents": {
    "cert_signers": {
      "zeroid.bit": [ "1iD5ZQJMNXu43w1qLB8sfdHVKppVMduGz" ]
    },
    "permission_rules": {
      ".*": {
        "files_allowed": "data.json",
        "max_size": 10000
      },
      "bitmsg/.*@zeroid.bit": { "max_size": 15000 }
    },
    "permissions": {
      "bad@zeroid.bit": false,
      "nofish@zeroid.bit": { "max_size": 100000 }
    }
  }
}
  • "ignore": ".*",: When the site owner signing this content.json don't add any file to it, they will be signed by the users.
  • cert_signers: We accept *@zeroid.bit users and they have to come with a cert that is has to signed by 1iD5ZQJMNXu43w1qLB8sfdHVKppVMduGz address.
  • permission_rules: We give 10kbytes of space to every user (15kb if registered using bitmessage)
  • permissions: Per-user permissions: ban bad@zeroid.bit user and allow 100k storage to nofish@zeroid.bit user. ( it's me :) )

After we saved this file we have to modify our root content.json to also ignore files in this directory and load the file containing the rules:

  ...
  "ignore": "data/.*",
  "includes": {
    "data/users/content.json": {
      "signers": [],
      "signers_required": 1
    }
  },
  ...

Note: You can give moderation permissions to other users by adding addresses to "signers" list.

Now we have to sign the data/users/content.json file using the following command: zeronet.py siteSign [siteaddress] --inner_path data/users/content.json

添加消息到我们的 json 文件

When hitting the Send button we going to add the message to our data.json file, sign it, then publish it to other users.

  • First add the onclick="return Page.sendMessage()" event listener to Send input button html tag.

  • Then create a new function in ZeroChat.coffee:

    sendMessage: =>
        if not Page.site_info.cert_user_id  # No account selected, display error
            Page.cmd "wrapperNotification", ["info", "Please, select your account."]
            return false

        inner_path = "data/users/#{@site_info.auth_address}/data.json"  # This is our data file

        # Load our current messages
        @cmd "fileGet", {"inner_path": inner_path, "required": false}, (data) =>
            if data  # Parse if already exits
                data = JSON.parse(data)
            else  # Not exits yet, use default data
                data = { "message": [] }

            # Add the message to data
            data.message.push({
                "body": document.getElementById("message").value,
                "date_added": (+new Date)
            })

            # Encode data array to utf8 json text
            json_raw = unescape(encodeURIComponent(JSON.stringify(data, undefined, '\t')))

            # Write file to disk
            @cmd "fileWrite", [inner_path, btoa(json_raw)], (res) =>
                if res == "ok"
                    # Publish the file to other users
                    @cmd "sitePublish", {"inner_path": inner_path}, (res) =>
                        document.getElementById("message").value = ""  # Reset the message input
                else
                    @cmd "wrapperNotification", ["error", "File write error: #{res}"]

        return false
  • After this is done type something to the message input and press the Send! button! You should see the message in the data/users/[your auth address]/data.json file.

创建数据库

Now we can save and publish our messages to other users, let's display it in our application! The best way to do this is map all data.json files to an SQL database.

The ZeroNet automatically do this for you, all you need is a dbschema.json file in your site's directory that describe your table structure:

{
    "db_name": "ZeroChat",
    "db_file": "data/zerochat.db",
    "version": 2,
    "maps": {
        "users/.+/data.json": {
            "to_table": [ "message" ]
        },
        "users/.+/content.json": {
            "to_keyvalue": [ "cert_user_id" ]
        }
    },
    "tables": {
        "message": {
            "cols": [
                ["body", "TEXT"],
                ["date_added", "INTEGER"],
                ["json_id", "INTEGER REFERENCES json (json_id)"]
            ],
            "indexes": ["CREATE UNIQUE INDEX message_key ON message(json_id, date_added)"],
            "schema_changed": 1
        }
    }
}
  • "db_name": "ZeroChat": Used only for debugging
  • "db_file": "data/zerochat.db": The SQLite database file will be stored here
  • "version": 2: Define the json table structure, version 2 is better suited to ZeroID based sites. More info in the reference docs.
  • "maps": {: Describe the json files -> table conversion
  • "users/.+/data.json": { "to_table": [ "message" ] }: Put the data from every user data.json file message node to message table.
  • "users/.+/content.json": { "to_keyvalue": [ "cert_user_id" ] }: Store the user's authentication id in simple key/value structure.
  • "tables": {: Describe the table and indexing structure.
  • ["json_id", "INTEGER REFERENCES json (json_id)"]: Every table should contain a json_id column, it defines the source file path.
  • "schema_changed": 1: Increment this when you change the table structure, so the peers can drop the table and re-create it from the json files.

Tip: For the best performance always create an index for json_id column in your tables, because when new file arrives the data will be updated based on this column.

Execute zeronet.py dbRebuild [your site address] command to generate database from current files.

The data/zerochat.db SQLite file will be created, to browse these files I recommend using SQLiteStudio (it's free and opensource)

At this point you have to restart your ZeroNet client to detect and manage your site's newly created database.

显示消息

As the messages are now it the SQL database we can query them using the dbQuery command:

    loadMessages: ->
        @cmd "dbQuery", ["SELECT * FROM message ORDER BY date_added"], (messages) =>
            document.getElementById("messages").innerHTML = ""  # Always start with empty messages
            for message in messages
                @addLine message.body

Add the @loadMessages() line to onOpenWebsocket function, then reload the page and you should see the messages you typed in.

To make it "real time" and display new messages immediately as they come in you have to add the @loadMessages() to the route part:

    route: (cmd, message) ->
        if cmd == "setSiteInfo"
            if message.params.cert_user_id
                document.getElementById("select_user").innerHTML = message.params.cert_user_id
            else
                document.getElementById("select_user").innerHTML = "Select user"
            @site_info = message.params  # Save site info data to allow access it later

            # Reload messages if new file arrives
            if message.params.event[0] == "file_done"
                @loadMessages()

And also reload the data when we submit a new message:

    sendMessage: =>
        ...
            # Write file to disk
            @cmd "fileWrite", [inner_path, btoa(json_raw)], (res) =>
                @loadMessages()
                ...
        ...

That's it! Now the messages are updated in real-time! You can try it by opening an another browser window and enter a messages there.

显示 ZeroID 用户名

To display the sender user name we have to create a more complex SQL query, because the user names are stored in content.json file and the messages are in data.json.

+ we will also escape the incoming messages to disallow html codes.

    loadMessages: ->
        query = """
            SELECT message.*, keyvalue.value AS cert_user_id FROM message
            LEFT JOIN json AS data_json USING (json_id)
            LEFT JOIN json AS content_json ON (
                data_json.directory = content_json.directory AND content_json.file_name = 'content.json'
            )
            LEFT JOIN keyvalue ON (keyvalue.key = 'cert_user_id' AND keyvalue.json_id = content_json.json_id)
            ORDER BY date_added
        """
        @cmd "dbQuery", [query], (messages) =>
            document.getElementById("messages").innerHTML = ""  # Always start with empty messages
            for message in messages
                body = message.body.replace(/</g, "&lt;").replace(/>/g, "&gt;")  # Escape html tags in body
                @addLine "<b>#{message.cert_user_id}</b>: #{body}"

最后的更改

  • We can remove the site siteInfo and serverInfo debug messages:
    onOpenWebsocket: (e) =>
        @cmd "siteInfo", {}, (site_info) =>
            # Update currently selected username
            if site_info.cert_user_id
                document.getElementById("select_user").innerHTML = site_info.cert_user_id
            @site_info = site_info  # Save site info data to allow access it later
        @loadMessages()
  • Send messages by hitting Enter:
<input type="text" id="message" onkeypress="if (event.keyCode == 13) Page.sendMessage()">
  • And add some CSS style to make it look better
<style>
* { font-family: monospace; line-height: 1.5em; font-size: 13px; vertical-align: middle; }
body { background-color: white; }
input#message { padding: 5px; width: 50%; height: 34px; }
input#send { height: 34px; margin-left: -2px; }
ul { padding: 0px; }
li { list-style-type: none; border-top: 1px solid #eee; padding: 5px 0px; }
li:nth-child(odd) { background-color: #F9FAFD; }
li b { color: #3F51B5; }
</style> 
  • After you edited any file never forget to sign and publish the modifications!
zeronet.py siteSign [your site address] --publish

恭喜! Now you have a server-less, pure P2P, SQL backed chat application! :)

本站已注册.bit域名

- Posted in YSC's blog by with comments

感谢 @domains4free 为我注册的域名 ysc3839.bit

教程参考: 东先生的ZeroBlog - 在ZeroNet注册一个免费的.bit域名 0List Blog - Free .bit Domains

[翻译]ZeroNet 站点开发教程 #1

- Posted in YSC's blog by with comments

原文链接: ZeroNet site development tutorial #1

这篇教程示范如何创建一个简单、多用户、p2p 的 ZeroNet 聊天室应用。

在此,第一部分我们将要创建一个新的站点并添加一些简单的 html 和 javascript 与 ZeroNet 交互(that interact to the ZeroNet client) 使用 ZeroFrame API。

创建一个新网站

  • 在你的 ZeroNet 目录下打开一个控制台
  • 执行 zeronet.py siteCreate 命令 (或者 ..\python\python.exe zeronet.py siteCreate 如果你使用 ZeroBundle)
  • 一个新的站点将会被创建同时私钥 (private key) 会显示在控制台窗口中。 保存下来,你只能用此私钥来修改你的站点!
  • 输入: yes

就是这样! 你的站点将会显示在 ZeroHello 界面。

现在让我们填入内容 (fill it with content)...


在调试模式中启动 ZeroNet

这是一个可选步骤,但建议这么做,因为此教程(以及所有示例)代码是使用 CoffeeScript 编写的。 调试模式会自动 (features automatic) 把 coffeescript 转换到 javascript,所以你不需要手动操作。 - 关闭 ZeroNet - 使用 zeronet.py --debug 命令启动 ZeroNet

(注意: 已经附带了用于 Windows 的 coffeescript 编译器,如果使用其他平台你需要使用 npm install -g coffee-script 来安装它)

编辑 index.html

创建一个简单的 html 结构 (structure) 以显示消息:

  • 转到 data/[yoursiteaddress]
  • 编辑 index.html:
<html>
<body>
<input type="text" id="message"><input type="button" id="send" value="Send!"/>
<ul id="messages">
 <li>Welcome to ZeroChat!</li>
</ul>
</body>
</html>

第一个 ZeroFrame API 调用

Note: 要禁用浏览器的缓存请保持 JavaScript 控制台开启 (F12)

ZeroFrame API 允许你获取 (query) 服务器/站点/用户信息,加载或修改文件通过一个 WebSocket 连接到你的 ZeroNet 客户端。

创建 js 以及 js/lib 目录然后下载并复制 ZeroFrame.coffee 文件到 js/lib,此文件帮助你使用 ZeroFrame API - 创建 js/ZeroChat.coffee 文件并写入一下内容:

class ZeroChat extends ZeroFrame
    init: ->
        @addLine "inited!"

    addLine: (line) ->
        messages = document.getElementById("messages")
        messages.innerHTML = "<li>#{line}</li>"+messages.innerHTML


    # Wrapper websocket connection ready
    onOpenWebsocket: (e) =>
        @cmd "serverInfo", {}, (server_info) =>
            @addLine "serverInfo response: <pre>" + JSON.stringify(server_info,null,2) + "</pre>"
        @cmd "siteInfo", {}, (site_info) =>
            @addLine "siteInfo response: <pre>" + JSON.stringify(site_info,null,2) + "</pre>"

window.Page = new ZeroChat()

此代码将会在 websocket 连接就绪时获取所有有关站点和服务器 (你的 ZeroNet 客户端) 的信息,然后将结果以格式化的 json 文本添加到 messages html 节点 (node)。 - 添加 <script type="text/javascript" src="js/all.js" async></script> 行在 </body>之前于 index.html 文件以自动编译并合并所有 js 目录中的 .coffee 文件到一个单独的 all.js 文件。

  • 如果一切顺利它应该是这样的:

Tutorial 并且你应该有以下目录结构:

data/[your site address]
├─ js/
│  ├─ lib/
│  │  └─ ZeroFrame.coffee
│  ├─ ZeroChat.coffee
│  └─ all.js
├── index.html
└── content.json
  • 每当你完成对你站点的修改你需要签署发布新内容使用 zeronet.py siteSign [yoursiteaddress] --publish 命令