-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathcommand.cr
More file actions
85 lines (76 loc) · 3.25 KB
/
command.cr
File metadata and controls
85 lines (76 loc) · 3.25 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
require "./application"
require "../placeos-core/module_manager"
module PlaceOS::Core::Api
class Command < Application
base "/api/core/v1/command/"
property module_manager : ModuleManager { ModuleManager.instance }
# Loads if not already loaded
# If the module is already running, it will be updated to latest settings.
@[AC::Route::POST("/:module_id/load")]
def load(
@[AC::Param::Info(description: "the module id we want to load", example: "mod-1234")]
module_id : String,
) : Nil
mod = Model::Module.find(module_id)
raise Error::NotFound.new("module #{module_id} not found in database") unless mod
module_manager.load_module(mod)
end
# Executes a command against a module
@[AC::Route::POST("/:module_id/execute")]
def execute(
@[AC::Param::Info(description: "the module id we want to send an execute request to", example: "mod-1234")]
module_id : String,
@[AC::Param::Info(description: "the user context for the execution", example: "user-1234")]
user_id : String? = nil,
) : Nil
# Check if module is loaded, or is a lazy module that can be loaded on demand
unless module_manager.process_manager(module_id, &.module_loaded?(module_id))
# Not loaded - check if it's a lazy module
unless module_manager.lazy_module?(module_id)
# Not a registered lazy module - check DB for launch_on_execute flag
mod = Model::Module.find(module_id)
unless mod && mod.launch_on_execute
Log.info { {module_id: module_id, message: "module not loaded"} }
raise Error::NotFound.new("module #{module_id} not loaded")
end
end
end
# NOTE:: we don't use the AC body helper for performance reasons.
# we're just proxying the JSON to the driver without parsing it
body = request.body.try &.gets_to_end
if body.nil?
message = "no request body provided"
Log.info { message }
raise Error::NotAcceptable.new(message)
end
execute_output = module_manager.process_manager(module_id) do |manager|
manager.execute(module_id, body, user_id: user_id)
end
# NOTE:: we are not using the typical response processing
# as we don't need to deserialise
response.content_type = "application/json"
if execute_output
response.headers[RESPONSE_CODE_HEADER] = execute_output[1].to_s
render text: execute_output[0]
else
render text: ""
end
end
# For now a one-to-one debug session to websocket should be fine as it's not
# a common operation and limited to system administrators
@[AC::Route::WebSocket("/:module_id/debugger")]
def module_debugger(
socket,
@[AC::Param::Info(description: "the module we want to debug", example: "mod-1234")]
module_id : String,
) : Nil
# Forward debug messages to the websocket
module_manager.process_manager(module_id, &.attach_debugger(module_id, socket))
end
@[AC::Route::Exception(PlaceOS::Driver::RemoteException, status_code: HTTP::Status::NON_AUTHORITATIVE_INFORMATION)]
def remote_exception(error) : CommonError
Log.error(exception: error) { "execute errored" }
CommonError.new(error)
end
end
end