# frozen_string_literal: truerequire'json'require'yaml'require'qeweney'require'papercraft'require'syntropy/errors'require'syntropy/file_watch'require'syntropy/module'moduleSyntropyclassAppclass<<selfdefload(opts)site_file_app(opts)||default_app(opts)endprivatedefsite_file_app(opts)site_fn=File.join(opts[:location],'_site.rb')returnnilif!File.file?(site_fn)loader=Syntropy::ModuleLoader.new(opts[:location],opts)loader.load('_site')enddefdefault_app(opts)new(opts[:machine],opts[:location],opts[:mount_path]||'/',opts)endenddefinitialize(machine,location,mount_path,opts={})@machine=machine@location=File.expand_path(location)@mount_path=mount_path@opts=opts@module_loader=Syntropy::ModuleLoader.new(@location,@opts)@router=Syntropy::Router.new(@opts,@module_loader)@machine.spindo# we do startup stuff asynchronously, in order to first let TP2 do its# setup tasks@machine.sleep0.15@opts[:logger]&.info(message: "Serving from #{File.expand_path(@location)}")@router.start_file_watcherifopts[:watch_files]endenddefcall(req)entry=@router[req.path]render_entry(req,entry)rescueSyntropy::Error=>emsg=e.messagereq.respond(msg.empty??nil:msg,':status'=>e.http_status)rescueStandardError=>epepe.backtracereq.respond(e.message,':status'=>Qeweney::Status::INTERNAL_SERVER_ERROR)endprivatedefrender_entry(req,entry)kind=entry[:kind]returnrespond_not_found(req)ifkind==:not_foundentry[:proc]||=calculate_route_proc(entry)entry[:proc].(req)enddefcalculate_route_proc(entry)render_proc=route_render_proc(entry)@router.calc_route_proc_with_hooks(entry,render_proc)enddefroute_render_proc(entry)caseentry[:kind]when:static->(req){respond_static(req,entry)}when:markdown->(req){respond_markdown(req,entry)}when:moduleload_module(entry)elseraise'Invalid entry kind'endenddefrespond_not_found(req)headers={':status'=>Qeweney::Status::NOT_FOUND}casereq.methodwhen'head'req.respond(nil,headers)elsereq.respond('Not found',headers)endenddefrespond_static(req,entry)entry[:mime_type]||=Qeweney::MimeTypes[File.extname(entry[:fn])]headers={'Content-Type'=>entry[:mime_type]}req.respond_by_http_method('head'=>[nil,headers],'get'=>->{[IO.read(entry[:fn]),headers]})enddefrespond_markdown(req,entry)entry[:mime_type]||=Qeweney::MimeTypes[File.extname(entry[:fn])]headers={'Content-Type'=>entry[:mime_type]}req.respond_by_http_method('head'=>[nil,headers],'get'=>->{[render_markdown(entry[:fn]),headers]})enddefrespond_module(req,entry)entry[:proc]||=load_module(entry)ifentry[:proc]==:invalidreq.respond(nil,':status'=>Qeweney::Status::INTERNAL_SERVER_ERROR)returnendentry[:proc].call(req)rescueSyntropy::Error=>ereq.respond(nil,':status'=>e.http_status)rescueStandardError=>epepe.backtracereq.respond(nil,':status'=>Qeweney::Status::INTERNAL_SERVER_ERROR)enddefload_module(entry)ref=entry[:fn].gsub(%r{^#{@location}/},'').gsub(/\.rb$/,'')o=@module_loader.load(ref)o.is_a?(Papercraft::Template)?wrap_template(o):orescueException=>e@opts[:logger]&.error(message: "Error while loading module #{ref}",error: e):invalidenddefwrap_template(template)lambda{|req|headers={'Content-Type'=>template.mime_type}req.respond_by_http_method('head'=>[nil,headers],'get'=>->{[template.render,headers]})}enddefrender_markdown(fn)atts,md=Syntropy.parse_markdown_file(fn,@opts)ifatts[:layout]layout=@module_loader.load("_layout/#{atts[:layout]}")html=layout.apply(**atts){emit_markdown(md)}.renderelsehtml=Papercraft.markdown(md)endhtmlendendend