# frozen_string_literal: true
module DiscourseAi
module AiBot
class ArtifactsController < ApplicationController
requires_plugin DiscourseAi::PLUGIN_NAME
before_action :require_site_settings!
skip_before_action :preload_json, :check_xhr, only: %i[show]
def show
artifact = AiArtifact.find(params[:id])
post = Post.find_by(id: artifact.post_id)
if artifact.public?
# no guardian needed
else
raise Discourse::NotFound if !post&.topic&.private_message?
raise Discourse::NotFound if !guardian.can_see?(post)
end
name = artifact.name
artifact_version = nil
if params[:version].present?
artifact_version = artifact.versions.find_by(version_number: params[:version])
raise Discourse::NotFound if !artifact_version
end
untrusted_html = build_untrusted_html(artifact_version || artifact, name)
trusted_html = build_trusted_html(artifact, artifact_version, name, untrusted_html)
set_security_headers
render html: trusted_html.html_safe, layout: false, content_type: "text/html"
end
private
def build_untrusted_html(artifact, name)
js = prepare_javascript(artifact.js)
<<~HTML
#{ERB::Util.html_escape(name)}
#{build_iframe_javascript}
#{artifact.html}
#{js}
HTML
end
def build_trusted_html(artifact, artifact_version, name, untrusted_html)
<<~HTML
#{ERB::Util.html_escape(name)}
#{build_parent_javascript(artifact)}
HTML
end
def prepare_javascript(js)
return "" if js.blank?
if !js.match?(%r{\A\s*}mi)
mod = ""
mod = " type=\"module\"" if js.match?(/\A\s*import.*/)
js = ""
end
js
end
def user_data
{
username: current_user ? current_user.username : nil,
user_id: current_user ? current_user.id : nil,
name: current_user ? current_user.name : nil,
}
end
def build_iframe_javascript
<<~JAVASCRIPT
JAVASCRIPT
end
def build_parent_javascript(artifact)
<<~JAVASCRIPT
JAVASCRIPT
end
def set_security_headers
response.headers.delete("X-Frame-Options")
response.headers[
"Content-Security-Policy"
] = "script-src 'self' 'unsafe-inline' 'wasm-unsafe-eval' #{AiArtifact::ALLOWED_CDN_SOURCES.join(" ")};"
response.headers["X-Robots-Tag"] = "noindex"
end
def require_site_settings!
if !SiteSetting.discourse_ai_enabled ||
!SiteSetting.ai_artifact_security.in?(%w[lax hybrid strict])
raise Discourse::NotFound
end
end
end
end
end