![](/wp-content/uploads/2019/12/boxy.png)
于是我自己的提问箱就是 https://ask.[data deleted]!
源代码在这里 #/boxy ?(给大家表演如何用一个项目气死你写前端和 Rust 的朋友(x
回答是人工的,并不是人工智能?(不过感觉加上人工智障能的话似乎也蛮好玩诶!
然后因为是模仿 peing 或者 sarahah 那样的匿名的提问箱,所以这个截图里就差不多是后台了~既然说是匿名的话,那就是真匿名~除了记录了提问时间,IP 只用来做了速度限制,没扔数据库。Question 在 MongoDB 里的模型则是
{ "_id" : ObjectId("5e0a0ca0001cd1ea00876f2e"), "question" : "What's this?", "question_time" : NumberLong(1577716896), "answer" : "It's a question box!", "answer_time" : NumberLong(1577716906), "id" : NumberLong(0) }
箱子的 owner 登录之后,直接点击答案的部分就进入编辑 / 回答模式,然后也可以删除提问什么的~
代码里倒是 Web 和纯 API 方式都实现了~然后回答之后自动生成 Twitter 卡片什么的还没有做_(:3」∠)_
再从技术层面上来说的话,这个项目用 Rust 上的 Hyper 作为 HTTP 服务器,然后数据库使用了 MongoDB,考虑到是作为单一用户的提问箱,因此用户名和密码是需要写在 boxy.json
里的~当然放进数据库里的是密码加盐后再 HMAC + SHA512 过的
stored_password = HMACSHA512(password + password_salt, password_salt)
{ "_id" : ObjectId("5e09a250004b7da70096e3dc"), "user" : "ryza", "password" : "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" }
Web 界面和后端做了分离,我自己写了一个简单的~因此就直接手写了,没有用到现在前端流行的 Vue 或者 Angular 之类的框架。访问后端的 API 也是直接用的 jQuery 的 AJAX 模块。
登录之后会返回一个 token
,包含了有效期,默认是 365 天,保存在了浏览器的 localStorage
里。登出的时候会自动清掉,但不是关闭页面 / 浏览器自动清除,所以在公用电脑上要记得登出~
那么下面是目前有的一些 API~ 以 http://localhost:5534
为例子!
1. 登录
API: http://localhost:5534/api/v1/token Method: POST { "type": "new", "username": "ryza", "password": HMACSHA512(HMACSHA512("password" + password_salt, password_salt) + time, token_salt), "time": 1577716906, }
在密码正确,且送达时 API Backend 的时间与计算 HMACSHA512 的时间相差 3 秒以内时就没问题。通过验证时可能的返回值如下~正确 status
肯定是 0,发生错误时 status
的值为相应的 HTTP 状态码,并且会有 reason
告知原因(后面所有的 API 均如此
{ "status": 0, "token": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", "expired_time": 1597716906, }
在发生错误时则可能是
{ "status": 403, "reason": "Access denied" }
2. 提问题
API: http://localhost:5534/api/v1/question
Method: POST
{
"question": "How to send questions?"
}
因为是匿名提问箱,所以提问不需要别的东西啦~只要有 question
就行~成功时会将对应的 Question ID 返回回来~
{ "status": 0, "id": 233 }
3. 回答问题
API: http://localhost:5534/api/v1/answer/{:id}
Method: POST
{
"answer": "Type anything you want to ask in the textarea above(*^3^)",
"token": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
}
需要注意这里的 API 的 URL,最后的 {:id}
替换成要回答的问题的 Question ID,比如要回答 ID 为 233
的问题的话,就是
http://localhost:5534/api/v1/answer/233
成功时只会返回 status
并且值为 0.
4. 删除问题
API: http://localhost:5534/api/v1/question/{:id}
Method: DELETE
{
"token": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
}
成功时只会返回 status
并且值为 0. 失败的话就参阅 reason
啦~
5. 获取某个问题的详情
API: http://localhost:5534/api/v1/question/{:id}
Method: GET
如果有 {:id}
对应的问题的话,将返回
{ "status": 0, "question": { "id": 233, "question_time": 1577718525, "question": "How to send questions?", "answer": "Type anything you want to ask in the textarea above(*^3^)", "answer_time": 1577718525 } }
6. 获取从某一 ID 之前的最多 30 个问题
API: http://localhost:5534/api/v1/question/from/{:id}
Method: GET
假如有 [1, 2, ..., 45] 个 问题的话,要拿到 33 之前(不包含 33)的 30 个问题则是
API: http://localhost:5534/api/v1/question/from/33
(如果中间有的问题被删掉了,则数满 30 个为止,或者没有更多在 33 之前的问题了)
{ "status": 0, "questions": [ { "id": 3, "question_time": 1577718523, "question": "How to build my own boxy?", "answer": "It's too long to answer here >_< But you can visit tis GitHub for usage ➜ #/boxy", "answer_time": 1577718556 }, { "id": 2, "question_time": 1577718522, "question": "How to send questions?", "answer": "Type anything you want to ask in the textarea above(*^3^)", "answer_time": 1577718532 }, { "id": 1, "question_time": 1577718448, "question": "What's this?", "answer": "It's a question box!", "answer_time": 1577718458 } ] }
7. 获取新的 Token
其实这个就是登录啦~
API: http://localhost:5534/api/v1/login Method: POST { "type": "new", "username": "ryza", "password": HMACSHA512(HMACSHA512("password" + password_salt, password_salt) + time, token_salt), "time": 1577716906, }
可能的返回值如下~
{ "status": 0, "token": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", "expired_time": 1597716906, }
8. 注销当前 Token
API: http://localhost:5534/api/v1/login Method: POST { "type": "revoke", "token": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", }
成功时只会返回 status
并且值为 0. 失败的话就参阅 reason
啦~
9. 注销所有 Token
API: http://localhost:5534/api/v1/login Method: POST { "type": "revoke_all", "token": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", }
成功时只会返回 status
并且值为 0. 失败的话就参阅 reason
啦~