identity-vault/app/controllers/docs_controller.rb
nora f02fceb531
VERSION. TWO. (#42)
* INITIAL SAML SUPPORT WOO YEA BABEY

it works?

* wawa

* mwaow

* b

* WOAG

* mph

* bunch more stuff

* new OAuth screen

* add trust level to oauth apps

* [community oauth] new scopes, validate only some community ones

* bleh

* my info first pass

* sessions and 2fa

* oauth authorizations/revoke

* nuke sms

* fix drift

* remove hcid on ident#edit

* attack our rack?

* session fixation't

* first pass at stepup auth

* eye eighteen en

* fix brand

* think that does it for dev mode!

* add promote to full user button

* first crack at landing page

* better sessions

* better id edit

* better verf

* less css pass 1

* add phone no

* better cssed?

* securité

* switch from slocks

* HCA

* touch last seen at

* session fingerprinting

* improved?

* localize scopes

* add proper oauth welcome

* eepier tutorial

* how long was that like that?!

* common blankslate

* better addresses?

* [backend] fix reprovisioning and promotion

* improve addresses

* ICONS, BEAUTIFUL ICONS

* primary sidebar

* saml welcome?

* new totp flow?

* marginally better login sec

* better print for backup codes!

* MASSIVE LINT PASS

* autocompletes

* woops

* new staging

* actual login code txnl

* no more legacy slack account linking

* fake slack in staging

* no account yet?

* add samls for staging

* fix slack_staging

* lint

* frickin' xmlsec

* no validate keys ?

* AUGH

* ASGJHFGSDJFG

* shoot me

* aieeeee

* SCHEIßE

* no more attempt association on code

* believe in prefers-color-scheme

* fix verf icon

* nuke vestigial aadhaar functionality

thanks deployor!

* fix xmlsec on gh ci

* remove identity (#27)

* move idcon flashes to locale

* remove dead code impersonation logic

h/t ian!

* fix hx-confirm on delete address?

* add missing dev app locale key

* fix #28

* wait, i'm an idiot (#28)

* THERE WE GO

* add paper_trail to more stuff

* red delete btn

* more red delete btns

* THE AUDIT LOGS UPDATE

* yuge lint pass

* Fix icons (#33)

Some icons didn't have a fill nor a viewbox

* weh

* first pass at docs

* memoize docs, fix 404

* [docs] add crappy erb support

* support non-e+ flow

* fix no devmode locale

* DOCS DOCS DOCS

* tldr dev doc

* anti-clickjacking countdown (h/t @J-Meow)

* weh

* get rid of those, they do nothing for us

* dependent destroy

* find user via scim if ent

* save nav channel ids

* fix base onboarding scenario

* only unique among the living

* add SAML debug

* simplify legacy_email

* add UAT env

* we ARE

* add slack to uat

* no entity id?

* fix saml if logged out

* fix scim assignment?

* bring channels into config

* darn it

* try backoff on assign_to_workspace?? this feels problematic

* do the scim docs lie?

* that was dumb

* Revert "do the scim docs lie?"

This reverts commit 69310dbef9476f2103d7a8280966a7fdf732129b.

* Revert "try backoff on assign_to_workspace?? this feels problematic"

This reverts commit 7a5edd67aa3836df1f31d628566e9ea69589c269.

* this some bull shit

* internal tutorial by default

* 18 point something

* fixes: componentize login, no more viewcontext, parse sp-initiated saml better

* one return to.

* just send it

* fix replay bug

* fix URL in welcome docs page (#38)

* simplify login/signup flow, s/faq/terms + privacy

* no more H... we hardly knew you

* first pass at reddening

* red pt. 2

* she's red for an AMAZING reason

* lint pass

* fix tooled tips

* another docs pass

* initial pass at factorybotting docs

* scope diffing for api docs!

* wait we don't need a legend lol

* add verf status to community apps

* fricken lint

* make current_user not nomethod

* move are_we_enterprise_yet to a flipper flag

* improve slack racing

* allow not creating slack

* factorybot in prod for api docs!

* LOL, LMAO

* properly set owner on oauthorizations

* lint pass

* bypass age on existing users

* fix that...

---------

Co-authored-by: Leo <leo@wilkin.xyz>
Co-authored-by: Tom (Deployor) <129990841+deployor@users.noreply.github.com>
Co-authored-by: DaInfLoop <github@dainfloop.is-a.dev>
2025-11-24 10:52:27 -05:00

150 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
erb_path = Rails.root.join("app", "views", "docs", "#{slug}.md.erb")
md_path = Rails.root.join("app", "views", "docs", "#{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