mirror of
https://github.com/System-End/identity-vault.git
synced 2026-04-20 00:25:21 +00:00
151 lines
4.4 KiB
Ruby
151 lines
4.4 KiB
Ruby
class DocsController < ApplicationController
|
|
layout "docs"
|
|
skip_before_action :authenticate_identity!, only: [ :index, :show ]
|
|
|
|
before_action :set_doc_path, only: :show
|
|
before_action :load_all_docs, only: [ :index, :show ]
|
|
|
|
def index
|
|
first_doc = @all_docs.first
|
|
return render plain: "No documentation found" if first_doc.nil?
|
|
|
|
redirect_to doc_path(slug: first_doc[:slug])
|
|
end
|
|
|
|
def show
|
|
raise ActionController::RoutingError, "Documentation not found" unless File.exist?(@doc_file_path)
|
|
|
|
@doc = parse_doc_file(@doc_file_path, params[:slug])
|
|
render :show
|
|
rescue StandardError => e
|
|
raise if e.is_a?(ActionController::RoutingError)
|
|
|
|
Rails.logger.error("Error parsing frontmatter for #{params[:slug]}: #{e.message}")
|
|
@doc = {
|
|
slug: params[:slug],
|
|
title: "Error",
|
|
content: "<p>Error parsing documentation.</p>"
|
|
}
|
|
render :show
|
|
end
|
|
|
|
private
|
|
|
|
def set_doc_path
|
|
slug = params[:slug] || "index"
|
|
|
|
# Sanitize the slug to prevent directory traversal
|
|
if slug.include?("..") || slug.include?("/")
|
|
raise ActionController::RoutingError, "Invalid documentation path"
|
|
end
|
|
|
|
docs_dir = Rails.root.join("app", "views", "docs")
|
|
erb_path = docs_dir.join("#{slug}.md.erb")
|
|
md_path = docs_dir.join("#{slug}.md")
|
|
|
|
@doc_file_path = File.exist?(erb_path) ? erb_path : md_path
|
|
end
|
|
|
|
def load_all_docs
|
|
@all_docs ||= begin
|
|
docs_dir = Rails.root.join("app", "views", "docs")
|
|
return [] unless Dir.exist?(docs_dir)
|
|
|
|
Dir.glob(docs_dir.join("*.md{,.erb}")).map do |file_path|
|
|
parsed = parse_frontmatter(file_path)
|
|
next unless parsed
|
|
|
|
slug = File.basename(file_path, ".*")
|
|
slug = File.basename(slug, ".*") if file_path.end_with?(".md.erb")
|
|
|
|
{
|
|
slug: slug,
|
|
title: parsed["title"] || slug.titleize,
|
|
category: parsed["category"],
|
|
order: parsed["order"] || 999,
|
|
hidden: parsed["hidden"] || false
|
|
}
|
|
rescue => e
|
|
Rails.logger.error("Error parsing doc #{file_path}: #{e.message}")
|
|
nil
|
|
end.compact.reject { |doc| doc[:hidden] }.sort_by { |doc| doc[:order] }
|
|
end
|
|
end
|
|
|
|
def parse_frontmatter(file_path)
|
|
cache_key = "doc_frontmatter:#{file_path}:#{File.mtime(file_path).to_i}"
|
|
Rails.cache.fetch(cache_key, expires_in: 1.hour) do
|
|
content = read_doc_content(file_path)
|
|
FrontMatterParser::Parser.new(:md).call(content)
|
|
end
|
|
end
|
|
|
|
def parse_doc_file(file_path, slug)
|
|
cache_key = "doc_content:#{file_path}:#{File.mtime(file_path).to_i}"
|
|
Rails.cache.fetch(cache_key, expires_in: 1.hour) do
|
|
content = File.read(file_path)
|
|
parsed = FrontMatterParser::Parser.new(:md).call(content)
|
|
|
|
# this is DISGUSTING, i'm sorry...
|
|
if file_path.to_s.end_with?(".erb")
|
|
erb_tags = []
|
|
content_with_placeholders = parsed.content.gsub(/<%=?.*?%>/m) do |match|
|
|
erb_tags << match
|
|
"ERB_PLACEHOLDER_#{erb_tags.length - 1}_PLACEHOLDER"
|
|
end
|
|
|
|
markdown_content = render_markdown(content_with_placeholders)
|
|
|
|
erb_tags.each_with_index do |erb_tag, index|
|
|
markdown_content = markdown_content.gsub("ERB_PLACEHOLDER_#{index}_PLACEHOLDER", erb_tag)
|
|
end
|
|
|
|
template = ERB.new(markdown_content)
|
|
final_content = template.result(view_context.instance_eval { binding })
|
|
else
|
|
final_content = render_markdown(parsed.content)
|
|
end
|
|
|
|
{
|
|
slug: slug,
|
|
title: parsed["title"] || "Untitled",
|
|
description: parsed["description"],
|
|
category: parsed["category"],
|
|
order: parsed["order"] || 999,
|
|
content: final_content.html_safe
|
|
}
|
|
end
|
|
end
|
|
|
|
def read_doc_content(file_path)
|
|
content = File.read(file_path)
|
|
if file_path.to_s.end_with?(".erb")
|
|
template = ERB.new(content)
|
|
template.result(view_context.instance_eval { binding })
|
|
else
|
|
content
|
|
end
|
|
end
|
|
|
|
def render_markdown(text)
|
|
renderer = Redcarpet::Render::HTML.new(
|
|
hard_wrap: true,
|
|
link_attributes: { target: "_blank", rel: "noopener noreferrer" }
|
|
)
|
|
|
|
markdown = Redcarpet::Markdown.new(
|
|
renderer,
|
|
autolink: false,
|
|
tables: true,
|
|
fenced_code_blocks: true,
|
|
strikethrough: true,
|
|
superscript: true,
|
|
highlight: true,
|
|
footnotes: true,
|
|
no_intra_emphasis: true,
|
|
disable_indented_code_blocks: true
|
|
)
|
|
|
|
markdown.render(text).html_safe
|
|
end
|
|
end
|