mirror of
https://github.com/l5yth/potato-mesh.git
synced 2026-03-28 17:42:48 +01:00
Add POST /api/messages and enforce API token (#56)
This commit is contained in:
13
README.md
13
README.md
@@ -20,11 +20,8 @@ what works:
|
||||
* displaying new node notifications and chat messages in default channel in chat box
|
||||
* displaying active node count and filtering nodes by name
|
||||
* exposing nodes and messages to api endpoints
|
||||
|
||||
what does not work _(yet):_
|
||||
|
||||
* posting nodes and messages to the api endpoints _(wip)_
|
||||
|
||||
* posting nodes and messages to the api endpoints with authentication
|
||||
|
||||
## requirements
|
||||
|
||||
requires a meshtastic node connected (via serial) to gather mesh data and the meshtastic cli.
|
||||
@@ -78,7 +75,7 @@ Puma starting in single mode...
|
||||
* Listening on http://127.0.0.1:41447
|
||||
```
|
||||
|
||||
set `API_TOKEN` required for authorizations on the api post-endpoints (wip).
|
||||
set `API_TOKEN` required for authorizations on the api post-endpoints.
|
||||
|
||||
the web app can be configured with environment variables (defaults shown):
|
||||
|
||||
@@ -100,8 +97,10 @@ the web app contains an api:
|
||||
|
||||
* GET `/api/nodes?limit=1000` - returns the latest 1000 nodes reported to the app
|
||||
* GET `/api/messages?limit=1000` - returns the latest 1000 messages
|
||||
* POST `/api/nodes` - upserts nodes provided as JSON object mapping node ids to node data (requires `Authorization: Bearer <API_TOKEN>`)
|
||||
* POST `/api/messages` - appends messages provided as a JSON object or array (requires `Authorization: Bearer <API_TOKEN>`)
|
||||
|
||||
the `POST` apis are _currently being worked on (tm)._
|
||||
the `API_TOKEN` environment variable must be set to a non-empty value and match the token supplied in the `Authorization` header for `POST` requests.
|
||||
|
||||
## license
|
||||
|
||||
|
||||
43
web/app.rb
43
web/app.rb
@@ -141,7 +141,29 @@ end
|
||||
def require_token!
|
||||
token = ENV["API_TOKEN"]
|
||||
provided = request.env["HTTP_AUTHORIZATION"].to_s.sub(/^Bearer\s+/i, "")
|
||||
halt 403, { error: "Forbidden" }.to_json unless token && provided == token
|
||||
halt 403, { error: "Forbidden" }.to_json unless token && !token.empty? && provided == token
|
||||
end
|
||||
|
||||
def insert_message(db, m)
|
||||
rx_time = m["rx_time"]&.to_i || Time.now.to_i
|
||||
rx_iso = m["rx_iso"] || Time.at(rx_time).utc.iso8601
|
||||
row = [
|
||||
rx_time,
|
||||
rx_iso,
|
||||
m["from_id"],
|
||||
m["to_id"],
|
||||
m["channel"],
|
||||
m["portnum"],
|
||||
m["text"],
|
||||
m["snr"],
|
||||
m["rssi"],
|
||||
m["hop_limit"],
|
||||
m["raw_json"],
|
||||
]
|
||||
db.execute <<~SQL, row
|
||||
INSERT INTO messages(rx_time,rx_iso,from_id,to_id,channel,portnum,text,snr,rssi,hop_limit,raw_json)
|
||||
VALUES (?,?,?,?,?,?,?,?,?,?,?)
|
||||
SQL
|
||||
end
|
||||
|
||||
post "/api/nodes" do
|
||||
@@ -162,6 +184,25 @@ ensure
|
||||
db&.close
|
||||
end
|
||||
|
||||
post "/api/messages" do
|
||||
require_token!
|
||||
content_type :json
|
||||
begin
|
||||
data = JSON.parse(request.body.read)
|
||||
rescue JSON::ParserError
|
||||
halt 400, { error: "invalid JSON" }.to_json
|
||||
end
|
||||
messages = data.is_a?(Array) ? data : [data]
|
||||
halt 400, { error: "too many messages" }.to_json if messages.size > 1000
|
||||
db = SQLite3::Database.new(DB_PATH)
|
||||
messages.each do |msg|
|
||||
insert_message(db, msg)
|
||||
end
|
||||
{ status: "ok" }.to_json
|
||||
ensure
|
||||
db&.close
|
||||
end
|
||||
|
||||
get "/" do
|
||||
erb :index, locals: {
|
||||
site_name: SITE_NAME,
|
||||
|
||||
Reference in New Issue
Block a user