ZeroBlog翻译

在本教程中,我们将建立一个P2P,分散,服务器和后端的聊天网站在不到100行代码。

(这是对原始教程的简化和纯javascript修改。)

 

创建新站点

  1. 点击⋮ > “在站点上创建新的空站点”菜单项。
  2. 你将被重定向到一个全新的网站,只有你可以修改!
  3. 将右上角的“0”按钮向左拖动以显示侧边栏
  4. 在底部,将站点标题更改为“My ZeroChat tutorial”,然后按“Save Site settings”。

聊天网站的HTML代码

  1. 在您喜爱的编辑器中打开“data/[yoursiteaddress]/index.html”文件。
  2. 将<body>标记的内容更改为:
<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>

js/ZeroFrame.js文件(如果您使用zerohhello创建站点,则会自动绑定)包含ZeroFrame类,该类允许我们与基于websocket的zeroframeapi通信。 

第一个ZeroFrame API调用

  • 在<script>块中初始化我们的应用程序,在ZeroFrame.js包括:
<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>
  •  如果我们重新加载页面,我们会看到一个“准备调用ZeroFrame API!”信息。
  • 要使“选择用户”按钮显示用户选择对话框,请向ZeroChat类添加以下功能:
    selectUser () {
        this.cmd("certSelect", {accepted_domains: ["zeroid.bit"]})
        return false
    }

显示用户当前的ZeroID帐户

当某些更改影响了站点(新内容到达、用户更改等)时,websocket事件将被推送到浏览器中(格式与查询siteInfo命令相同)

  • 要处理此事件,请将此添加到我们的类中:
 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
        }
    }

此代码将实时更新当前选定的用户名。

要在页面加载时也显示当前选定的用户名,请将此添加到“onOpenWebsocket”函数: 

 

 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
        })

现在我们的应用程序应该总是正确地显示当前选择的用户(即使从其他浏览器窗口修改)

设置用户内容权限

为了允许其他用户在我们的网站上发表文章,我们必须定义第三方内容的规则。 

在站点目录中创建data/users/content.json文件: 

{
	"files": {},
	"ignore": ".*",	// 此目录中的文件将由用户签名,而不是由网站所有者签名。
	"modified": 0.0,
	"signs": {},
	"user_contents": {
		
		// 我们接受*@zeroid.bit用户,他们必须提供一个证书,该证书必须由1iD5ZQJMNXu43w1qLB8sfdHVKppVMduGz地址签名。
		"cert_signers": {
			"zeroid.bit": [ "1iD5ZQJMNXu43w1qLB8sfdHVKppVMduGz" ]
		},
		
		// 我们为每个用户提供10KB的空间(如果使用bitmessage注册,则为15kb)
		"permission_rules": {
			".*": {
				"files_allowed": "data.json",
				"max_size": 10000
			},
			"bitmsg/.*@zeroid.bit": { "max_size": 15000 }
		},
		
		// 每用户权限:禁止“bad@zeroid.bit“用户并允许10万存储”nofish@zeroid.bit“用户。(是我:))
		"permissions": {
			"bad@zeroid.bit": false,
			"nofish@zeroid.bit": { "max_size": 100000 }
		}
	}
}

保存此文件后,我们还必须修改站点的root content.json,以便在签名时忽略此目录中的所有内容,并加载包含以下规则的文件: 

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

注意:您可以通过向“签名者”列表中添加地址来向其他用户授予仲裁权限。 

  • 通过按侧边栏上的“Sign”按钮对root content.json修改进行签名。
  • 然后保持侧边栏打开,并将“content.json”更改为“data/users/content.json”,然后再次按“Sign”按钮!

向json文件添加消息 

当点击发送按钮时,我们会将消息添加到用户的 data.json 文件中,对其进行签名,然后将其发布给其他用户。

在 ZeroChat 类中创建一个新函数:

 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 data_inner_path = "data/users/" + this.site_info.auth_address + "/data.json"
        var content_inner_path = "data/users/" + this.site_info.auth_address + "/content.json"

        // Load our current messages
        this.cmd("fileGet", {"inner_path": data_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", [data_inner_path, btoa(json_raw)], (res) => {
                if (res == "ok") {
                    // Reset the message input
                    document.getElementById("message").value = ""
                    // Sign the changed file in our user's directory
                    this.cmd("siteSign", {"inner_path": content_inner_path}, (res) => {
                        // Publish to other users
                        this.cmd("sitePublish", {"inner_path": content_inner_path, "sign": false})
                    })
                } else {
                    this.cmd("wrapperNotification", ["error", "File write error: #{res}"])
                }
            })
        })

        return false
    }

在此修改后,在消息输入中输入一些内容,然后按发送按钮! 您应该会在 data/users/[your auth address]/data.json 文件中看到该消息。(如果您看到“发布失败”消息,请不要担心:这是正常的,因为我们的网站上还没有任何其他用户)

创建数据库  

现在我们可以保存和发布我们的消息给其他用户,让我们在我们的应用程序中显示它! 最好的方法是将所有 data.json 文件映射到 SQL 数据库。

ZeroNet 会自动为您执行此操作,您只需要站点目录中的 dbschema.json 文件来描述您的表结构:  

{
    "db_name": "ZeroChat",    // 仅用于调试 
    "db_file": "data/zerochat.db",    // SQLite 数据库文件将存储在这里 
    "version": 2,    // 定义 json 表结构,版本 2 更适合基于 ZeroID 的站点。 参考文档中的更多信息。 
    // 描述json文件->表转换 
    "maps": {
        // 将每个用户的 data.json 文件消息节点中的数据放入消息表。 
        "users/.+/data.json": {
            "to_table": [ "message" ]
        },
        // 为了方便 SQL Join,将用户名存储在 data.json 文件的 json 表条目中。 
        "users/.+/content.json": {
            "to_json_table": [ "cert_user_id" ],
            "file_name": "data.json"
        }
    },
    // 描述表和索引结构。 将为每个 json 文件自动创建一个 json 表条目。
    "tables": {
        "json": {
            "cols": [
                ["json_id", "INTEGER PRIMARY KEY AUTOINCREMENT"],    // 每个表都应该包含一个 json_id 列,它定义了源文件路径。 
                ["directory", "TEXT"],
                ["file_name", "TEXT"],
                ["cert_user_id", "TEXT"]
            ],
            "indexes": ["CREATE UNIQUE INDEX path ON json(directory, file_name)"],
            "schema_changed": 10    // 更改表结构时增加此值,以便对等方可以删除表并从 json 文件重新创建它。 
        },
        "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    // 更改表结构时增加此值,以便对等方可以删除表并从 json 文件重新创建它。 
        }
    }
}

提示:为了获得最佳性能,请始终为表中的 json_id 列创建索引,因为当新文件到达时,数据将基于此列进行更新。

  • 按 Reload,然后按侧边栏上的 Rebuild 按钮以生成数据库。  

如果一切顺利,就会创建 data/zerochat.db SQLite 文件。 要浏览这些文件,我建议使用 SQLiteStudio(它是免费和开源的)  

显示消息  

消息被加载到 SQL 数据库中,因此我们可以使用 dbQuery 命令轻松查询它们: 

    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)
            }
        })
    }

 将 this.loadMessages() 添加到 onOpenWebsocket 函数,然后重新加载页面,您应该会看到您输入的消息。

要使其“实时”并在新消息进来时立即显示,您还必须将 this.loadMessages() 添加到 onRequest 函数:

 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()
        }
    }

当我们提交新消息时也重新加载数据:  

    sendMessage () {
        ...
                    // Sign the changed file in our user's directory
                    this.cmd("siteSign", {"inner_path": content_inner_path}, (res) => {
                        this.loadMessages() // Reload messages
                        ...
        ...

 就是这样! 现在消息是实时更新的! 您可以通过打开另一个浏览器窗口并在那里输入消息来尝试。

最后添加

  • 按 Enter 发送消息  
<input type="text" id="message" onkeypress="if (event.keyCode == 13) page.sendMessage()">
  •  并添加一些 CSS 样式以使其看起来更好
<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>

 恭喜! 您已完成 ZeroChat 教程! :)

一些改进的想法:

  • 显示消息时间
  • 将源代码移动到单独的 .js 文件
  • 限制显示消息/添加分页
  • Markdown 格式
  • 用户名提及  

提示:.js 文件被缓存。 为避免这种情况,您需要在选中“禁用缓存”的情况下保持打开开发控制台 (F12)。  

提示:如果您对源代码进行了任何修改,请不要忘记重新签名和发布您的网站。  

提示:您站点的私钥在 data/users.json 文件中(搜索您站点的地址,然后会有一个私钥条目),所以最好备份它!  

提示:ZeroNet 为开发人员提供了内置的 .js 和 .css 合并功能。 要启用它,请将调试添加到 zeronet.conf 文件的 [global] 部分,然后如果您请求 all.js 或 all.css,它将响应目录中的每个文件的内容  

 

 

 

 

 

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值