site_files.rb 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263
  1. get '/site_files/new_page' do
  2. require_login
  3. @title = 'New Page'
  4. erb :'site_files/new_page'
  5. end
  6. # Redirect from original path
  7. get '/site_files/new' do
  8. require_login
  9. redirect '/site_files/new_page'
  10. end
  11. post '/site_files/create' do
  12. require_login
  13. @errors = []
  14. filename = params[:filename]
  15. filename.gsub!(/[^a-zA-Z0-9_\-.]/, '')
  16. redirect_uri = '/dashboard'
  17. redirect_uri += "?dir=#{Rack::Utils.escape params[:dir]}" if params[:dir]
  18. if filename.nil? || filename.strip.empty?
  19. flash[:error] = 'You must provide a file name.'
  20. redirect redirect_uri
  21. end
  22. name = "#{filename}"
  23. name = "#{params[:dir]}/#{name}" if params[:dir]
  24. name = current_site.scrubbed_path name
  25. if current_site.file_exists?(name)
  26. flash[:error] = %{Web page "#{Rack::Utils.escape_html name}" already exists! Choose another name.}
  27. redirect redirect_uri
  28. end
  29. extname = File.extname name
  30. unless extname.match /^\.#{Site::EDITABLE_FILE_EXT}/i
  31. flash[:error] = "Must be an text editable file type (#{Site::VALID_EDITABLE_EXTENSIONS.join(', ')})."
  32. redirect redirect_uri
  33. end
  34. site_file = current_site.site_files_dataset.where(path: name).first
  35. if site_file
  36. flash[:error] = 'File already exists, cannot create.'
  37. redirect redirect_uri
  38. end
  39. if extname.match(/^\.html|^\.htm/i)
  40. current_site.install_new_html_file name
  41. else
  42. file_path = current_site.files_path(name)
  43. FileUtils.touch file_path
  44. File.chmod 0640, file_path
  45. site_file ||= SiteFile.new site_id: current_site.id, path: name
  46. site_file.size = 0
  47. site_file.set size: 0
  48. site_file.set sha1_hash: Digest::SHA1.hexdigest('')
  49. site_file.set updated_at: Time.now
  50. site_file.save
  51. end
  52. escaped_name = Rack::Utils.escape_html name
  53. flash[:success] = %{#{escaped_name} was created! <a style="color: #FFFFFF; text-decoration: underline" href="/site_files/text_editor/#{escaped_name}">Click here to edit it</a>.}
  54. redirect redirect_uri
  55. end
  56. def file_upload_response(error=nil)
  57. if error
  58. flash[:error] = error
  59. end
  60. if params[:from_button]
  61. query_string = params[:dir] ? "?"+Rack::Utils.build_query(dir: params[:dir]) : ''
  62. redirect "/dashboard#{query_string}"
  63. else
  64. halt 406, error if error
  65. halt 200, 'File(s) successfully uploaded.'
  66. end
  67. end
  68. def require_login_file_upload_ajax
  69. file_upload_response 'You are not signed in!' unless signed_in?
  70. end
  71. post '/site_files/delete' do
  72. require_login
  73. path = HTMLEntities.new.decode params[:filename]
  74. current_site.delete_file path
  75. flash[:success] = "Deleted #{Rack::Utils.escape_html params[:filename]}."
  76. dirname = Pathname(path).dirname
  77. dir_query = dirname.nil? || dirname.to_s == '.' ? '' : "?dir=#{Rack::Utils.escape dirname}"
  78. redirect "/dashboard#{dir_query}"
  79. end
  80. post '/site_files/rename' do
  81. require_login
  82. path = HTMLEntities.new.decode params[:path]
  83. new_path = HTMLEntities.new.decode params[:new_path]
  84. site_file = current_site.site_files.select {|s| s.path == path}.first
  85. escaped_path = Rack::Utils.escape_html path
  86. escaped_new_path = Rack::Utils.escape_html new_path
  87. if site_file.nil?
  88. flash[:error] = "File #{escaped_path} does not exist."
  89. else
  90. res = site_file.rename new_path
  91. if res.first == true
  92. flash[:success] = "Renamed #{escaped_path} to #{escaped_new_path}"
  93. else
  94. flash[:error] = "Failed to rename #{escaped_path} to #{escaped_new_path}: #{Rack::Utils.escape_html res.last}"
  95. end
  96. end
  97. dirname = Pathname(path).dirname
  98. dir_query = dirname.nil? || dirname.to_s == '.' ? '' : "?dir=#{Rack::Utils.escape dirname}"
  99. redirect "/dashboard#{dir_query}"
  100. end
  101. get '/site_files/download' do
  102. require_login
  103. if !current_site.dl_queued_at.nil? && current_site.dl_queued_at > 1.hour.ago
  104. flash[:error] = 'Site downloads are currently limited to once per hour, please try again later.'
  105. redirect request.referer
  106. end
  107. content_type 'application/zip'
  108. attachment "neocities-#{current_site.username}.zip"
  109. current_site.dl_queued_at = Time.now
  110. current_site.save_changes validate: false
  111. directory_path = current_site.files_path
  112. stream do |out|
  113. ZipTricks::Streamer.open(out) do |zip|
  114. Dir["#{directory_path}/**/*"].each do |file|
  115. next if File.directory?(file)
  116. zip_path = file.sub("#{directory_path}/", '')
  117. zip.write_stored_file(zip_path) do |file_writer|
  118. File.open(file, 'rb') do |file|
  119. IO.copy_stream(file, file_writer)
  120. end
  121. end
  122. end
  123. end
  124. end
  125. end
  126. get %r{\/site_files\/download\/(.+)} do
  127. require_login
  128. dont_browser_cache
  129. not_found if params[:captures].nil? || params[:captures].length != 1
  130. filename = params[:captures].first
  131. attachment filename
  132. send_file current_site.current_files_path(filename)
  133. end
  134. get %r{\/site_files\/text_editor\/(.+)} do
  135. require_login
  136. dont_browser_cache
  137. @filename = params[:captures].first
  138. redirect '/site_files/text_editor?filename=' + Rack::Utils.escape(@filename)
  139. end
  140. get '/site_files/text_editor' do
  141. require_login
  142. dont_browser_cache
  143. @filename = params[:filename]
  144. extname = File.extname @filename
  145. @ace_mode = case extname
  146. when /htm|html/ then 'html'
  147. when /js/ then 'javascript'
  148. when /md/ then 'markdown'
  149. when /css/ then 'css'
  150. else
  151. nil
  152. end
  153. file_path = current_site.current_files_path @filename
  154. if File.directory? file_path
  155. flash[:error] = 'Cannot edit a directory.'
  156. redirect '/dashboard'
  157. end
  158. if !File.exist?(file_path)
  159. flash[:error] = 'We could not find the requested file.'
  160. redirect '/dashboard'
  161. end
  162. @title = "Editing #{@filename}"
  163. erb :'site_files/text_editor'
  164. end
  165. get '/site_files/allowed_types' do
  166. @title = 'Allowed File Types'
  167. erb :'site_files/allowed_types'
  168. end
  169. get '/site_files/hotlinking' do
  170. @title = 'Hotlinking Information'
  171. erb :'site_files/hotlinking'
  172. end
  173. get '/site_files/mount_info' do
  174. @title = 'Site Mount Information'
  175. erb :'site_files/mount_info'
  176. end
  177. post '/site_files/chat' do
  178. require_login
  179. dont_browser_cache
  180. headers 'X-Accel-Buffering' => 'no'
  181. halt(403) unless parent_site.supporter?
  182. # Ensure the request is treated as a stream
  183. stream do |out|
  184. url = 'https://api.anthropic.com/v1/messages'
  185. headers = {
  186. "anthropic-version" => "2023-06-01",
  187. "anthropic-beta" => "messages-2023-12-15",
  188. "content-type" => "application/json",
  189. "x-api-key" => $config['anthropic_api_key']
  190. }
  191. body = {
  192. model: "claude-3-haiku-20240307",
  193. system: params[:system],
  194. messages: JSON.parse(params[:messages]),
  195. max_tokens: 4096,
  196. temperature: 0.5,
  197. stream: true
  198. }.to_json
  199. res = HTTP.headers(headers).post(url, body: body)
  200. while(buffer = res.body.readpartial)
  201. out << buffer
  202. end
  203. end
  204. end