Files
ubicloud/routes/project/cli.rb
Jeremy Evans e54a74a63f Add Web Shell
Web shell allows access to the ubi command line program without
installing it. All features of ubi are available in the web shell,
except for the program execution features.

To avoid the need to create a personal access token to use the
web shell, the internal cli request sets clover.web_cli_session_id
in the request environment, and the internal request uses that
as the session id if that exists. So web cli requests are only
checked against the account's permissions, they are not checked
against a token's permissions. I think this approach is better
than forcing a user to create a personal access token to use the
web shell.

For ease of use, this uses the autofocus attribute, so add that as
a boolean attribute.
2025-08-20 14:46:27 -07:00

80 lines
2.4 KiB
Ruby

# frozen_string_literal: true
class Clover
ubi_version = File.read(File.expand_path("../../../cli/version.txt", __FILE__)).chomp.freeze
hash_branch(:project_prefix, "cli") do |r|
r.web do
r.is do
no_authorization_needed
r.get do
view "cli"
end
r.post do
no_audit_log
if (multi_cli = typecast_body_params.nonempty_str("multi-cli"))
multi_cli = multi_cli.split(/\r?\n/)
multi_cli.reject!(&:empty?)
@last_cli, @cli, *@clis = multi_cli
else
@last_cli = typecast_body_params.str!("cli")
r.POST.delete("cli")
if (@clis = typecast_body_params.array(:nonempty_str, "clis"))
@cli = @clis.shift
end
if (confirm = typecast_body_params.nonempty_str("confirm"))
@last_cli = "--confirm #{confirm.inspect} #{@last_cli}"
end
end
@last_cli = @last_cli.sub(/\A\s*ubi\s+/, "")
argv = @last_cli.shellsplit
env["clover.project_id"] = @project.id
env["clover.project_ubid"] = @project.ubid
env["clover.web_cli_session_id"] = rodauth.session_value
env["HTTP_X_UBI_VERSION"] = ubi_version
env["CONTENT_TYPE"] = "application/json"
# Need to save the host and restore it afterward for rack-test to work correctly
host = env["HTTP_HOST"]
env["HTTP_HOST"] = "api.ubicloud.com"
_, headers, body = UbiCli.process(argv, env)
env["HTTP_HOST"] = host
body = body.join
@output = if (@ubi_command_execute = headers["ubi-command-execute"])
h("$ #{body.split("\0").prepend(@ubi_command_execute).shelljoin}")
else
ubids = {}
body.scan(UbiCli::OBJECT_INFO_REGEXP) { ubids[UBID.to_uuid(it[0])] ||= nil }
UBID.resolve_map(ubids)
h(body).gsub(UbiCli::OBJECT_INFO_REGEXP) do
if (obj = ubids[UBID.to_uuid(it)]) && obj.respond_to?(:path)
"<a class=\"text-orange-600\" href=\"#{@project.path}#{obj.path}\">#{it}</a>"
else
it
end
end
end
if (@ubi_confirm = headers["ubi-confirm"])
if @cli
@clis ||= []
@clis.prepend(@cli)
end
@cli = @last_cli
end
view "cli"
end
end
end
end
end