itsi 0.2.3 → 0.2.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (464) hide show
  1. checksums.yaml +4 -4
  2. data/Cargo.lock +2 -2
  3. data/README.md +1 -1
  4. data/Rakefile +2 -2
  5. data/crates/itsi_rb_helpers/src/lib.rs +0 -1
  6. data/crates/itsi_scheduler/Cargo.toml +1 -1
  7. data/crates/itsi_server/Cargo.toml +1 -1
  8. data/crates/itsi_server/src/server/middleware_stack/middlewares/compression.rs +28 -11
  9. data/crates/itsi_server/src/server/middleware_stack/middlewares/log_requests.rs +1 -1
  10. data/crates/itsi_server/src/server/middleware_stack/middlewares/proxy.rs +1 -2
  11. data/crates/itsi_server/src/server/middleware_stack/middlewares/static_assets.rs +14 -2
  12. data/crates/itsi_server/src/server/middleware_stack/middlewares/string_rewrite.rs +86 -41
  13. data/crates/itsi_server/src/services/itsi_http_service.rs +46 -35
  14. data/crates/itsi_server/src/services/static_file_server.rs +31 -3
  15. data/examples/api_with_schema_and_controllers/Itsi.rb +35 -0
  16. data/examples/api_with_schema_and_controllers/README.md +10 -0
  17. data/examples/api_with_schema_and_controllers/controllers.rb +66 -0
  18. data/examples/api_with_schema_and_controllers/schemas.rb +25 -0
  19. data/examples/file_server/Itsi.rb +30 -0
  20. data/examples/file_server/README.md +3 -0
  21. data/examples/file_server/about.html +80 -0
  22. data/examples/file_server/admin/secrets.txt +1 -0
  23. data/examples/file_server/fairytale.txt +33 -0
  24. data/examples/file_server/itsi-server-100.png +0 -0
  25. data/examples/file_server/subdirectory/movies.csv +21 -0
  26. data/examples/helpers/datastore.rb +84 -0
  27. data/examples/media_server/Itsi.rb +5 -0
  28. data/examples/media_server/README.md +3 -0
  29. data/examples/media_server/images/itsi-server-100.png +0 -0
  30. data/examples/media_server/index.html +82 -0
  31. data/examples/multi_rack_rails_sinatra/Gemfile +66 -0
  32. data/examples/multi_rack_rails_sinatra/Gemfile.lock +416 -0
  33. data/examples/multi_rack_rails_sinatra/Itsi.rb +44 -0
  34. data/examples/multi_rack_rails_sinatra/README.md +7 -0
  35. data/examples/multi_rack_rails_sinatra/index.html +119 -0
  36. data/examples/multi_rack_rails_sinatra/rails_subapp/.dockerignore +51 -0
  37. data/examples/multi_rack_rails_sinatra/rails_subapp/.gitattributes +9 -0
  38. data/examples/multi_rack_rails_sinatra/rails_subapp/.github/dependabot.yml +12 -0
  39. data/examples/multi_rack_rails_sinatra/rails_subapp/.github/workflows/ci.yml +90 -0
  40. data/examples/multi_rack_rails_sinatra/rails_subapp/.gitignore +34 -0
  41. data/examples/multi_rack_rails_sinatra/rails_subapp/.kamal/hooks/docker-setup.sample +3 -0
  42. data/examples/multi_rack_rails_sinatra/rails_subapp/.kamal/hooks/post-app-boot.sample +3 -0
  43. data/examples/multi_rack_rails_sinatra/rails_subapp/.kamal/hooks/post-deploy.sample +14 -0
  44. data/examples/multi_rack_rails_sinatra/rails_subapp/.kamal/hooks/post-proxy-reboot.sample +3 -0
  45. data/examples/multi_rack_rails_sinatra/rails_subapp/.kamal/hooks/pre-app-boot.sample +3 -0
  46. data/examples/multi_rack_rails_sinatra/rails_subapp/.kamal/hooks/pre-build.sample +51 -0
  47. data/examples/multi_rack_rails_sinatra/rails_subapp/.kamal/hooks/pre-connect.sample +47 -0
  48. data/examples/multi_rack_rails_sinatra/rails_subapp/.kamal/hooks/pre-deploy.sample +109 -0
  49. data/examples/multi_rack_rails_sinatra/rails_subapp/.kamal/hooks/pre-proxy-reboot.sample +3 -0
  50. data/examples/multi_rack_rails_sinatra/rails_subapp/.kamal/secrets +17 -0
  51. data/examples/multi_rack_rails_sinatra/rails_subapp/.rubocop.yml +8 -0
  52. data/examples/multi_rack_rails_sinatra/rails_subapp/.ruby-version +1 -0
  53. data/examples/multi_rack_rails_sinatra/rails_subapp/Dockerfile +72 -0
  54. data/examples/multi_rack_rails_sinatra/rails_subapp/Gemfile +66 -0
  55. data/examples/multi_rack_rails_sinatra/rails_subapp/Gemfile.lock +416 -0
  56. data/examples/multi_rack_rails_sinatra/rails_subapp/README.md +24 -0
  57. data/examples/multi_rack_rails_sinatra/rails_subapp/Rakefile +6 -0
  58. data/examples/multi_rack_rails_sinatra/rails_subapp/app/assets/images/.keep +0 -0
  59. data/examples/multi_rack_rails_sinatra/rails_subapp/app/assets/stylesheets/application.css +161 -0
  60. data/examples/multi_rack_rails_sinatra/rails_subapp/app/controllers/application_controller.rb +4 -0
  61. data/examples/multi_rack_rails_sinatra/rails_subapp/app/controllers/articles_controller.rb +70 -0
  62. data/examples/multi_rack_rails_sinatra/rails_subapp/app/controllers/concerns/.keep +0 -0
  63. data/examples/multi_rack_rails_sinatra/rails_subapp/app/controllers/home_controller.rb +4 -0
  64. data/examples/multi_rack_rails_sinatra/rails_subapp/app/helpers/application_helper.rb +2 -0
  65. data/examples/multi_rack_rails_sinatra/rails_subapp/app/helpers/articles_helper.rb +2 -0
  66. data/examples/multi_rack_rails_sinatra/rails_subapp/app/helpers/home_helper.rb +2 -0
  67. data/examples/multi_rack_rails_sinatra/rails_subapp/app/javascript/application.js +3 -0
  68. data/examples/multi_rack_rails_sinatra/rails_subapp/app/javascript/controllers/application.js +9 -0
  69. data/examples/multi_rack_rails_sinatra/rails_subapp/app/javascript/controllers/hello_controller.js +7 -0
  70. data/examples/multi_rack_rails_sinatra/rails_subapp/app/javascript/controllers/index.js +4 -0
  71. data/examples/multi_rack_rails_sinatra/rails_subapp/app/jobs/application_job.rb +7 -0
  72. data/examples/multi_rack_rails_sinatra/rails_subapp/app/mailers/application_mailer.rb +4 -0
  73. data/examples/multi_rack_rails_sinatra/rails_subapp/app/models/application_record.rb +3 -0
  74. data/examples/multi_rack_rails_sinatra/rails_subapp/app/models/article.rb +2 -0
  75. data/examples/multi_rack_rails_sinatra/rails_subapp/app/models/concerns/.keep +0 -0
  76. data/examples/multi_rack_rails_sinatra/rails_subapp/app/views/articles/_article.html.erb +14 -0
  77. data/examples/multi_rack_rails_sinatra/rails_subapp/app/views/articles/_article.json.jbuilder +2 -0
  78. data/examples/multi_rack_rails_sinatra/rails_subapp/app/views/articles/_form.html.erb +30 -0
  79. data/examples/multi_rack_rails_sinatra/rails_subapp/app/views/articles/edit.html.erb +12 -0
  80. data/examples/multi_rack_rails_sinatra/rails_subapp/app/views/articles/index.html.erb +29 -0
  81. data/examples/multi_rack_rails_sinatra/rails_subapp/app/views/articles/index.json.jbuilder +1 -0
  82. data/examples/multi_rack_rails_sinatra/rails_subapp/app/views/articles/new.html.erb +11 -0
  83. data/examples/multi_rack_rails_sinatra/rails_subapp/app/views/articles/show.html.erb +10 -0
  84. data/examples/multi_rack_rails_sinatra/rails_subapp/app/views/articles/show.json.jbuilder +1 -0
  85. data/examples/multi_rack_rails_sinatra/rails_subapp/app/views/home/index.html.erb +8 -0
  86. data/examples/multi_rack_rails_sinatra/rails_subapp/app/views/layouts/application.html.erb +34 -0
  87. data/examples/multi_rack_rails_sinatra/rails_subapp/app/views/layouts/mailer.html.erb +13 -0
  88. data/examples/multi_rack_rails_sinatra/rails_subapp/app/views/layouts/mailer.text.erb +1 -0
  89. data/examples/multi_rack_rails_sinatra/rails_subapp/app/views/pwa/manifest.json.erb +22 -0
  90. data/examples/multi_rack_rails_sinatra/rails_subapp/app/views/pwa/service-worker.js +26 -0
  91. data/examples/multi_rack_rails_sinatra/rails_subapp/bin/brakeman +7 -0
  92. data/examples/multi_rack_rails_sinatra/rails_subapp/bin/bundle +109 -0
  93. data/examples/multi_rack_rails_sinatra/rails_subapp/bin/dev +2 -0
  94. data/examples/multi_rack_rails_sinatra/rails_subapp/bin/docker-entrypoint +14 -0
  95. data/examples/multi_rack_rails_sinatra/rails_subapp/bin/importmap +4 -0
  96. data/examples/multi_rack_rails_sinatra/rails_subapp/bin/jobs +6 -0
  97. data/examples/multi_rack_rails_sinatra/rails_subapp/bin/kamal +27 -0
  98. data/examples/multi_rack_rails_sinatra/rails_subapp/bin/rails +4 -0
  99. data/examples/multi_rack_rails_sinatra/rails_subapp/bin/rake +4 -0
  100. data/examples/multi_rack_rails_sinatra/rails_subapp/bin/rubocop +8 -0
  101. data/examples/multi_rack_rails_sinatra/rails_subapp/bin/setup +34 -0
  102. data/examples/multi_rack_rails_sinatra/rails_subapp/bin/thrust +5 -0
  103. data/examples/multi_rack_rails_sinatra/rails_subapp/config/application.rb +27 -0
  104. data/examples/multi_rack_rails_sinatra/rails_subapp/config/boot.rb +4 -0
  105. data/examples/multi_rack_rails_sinatra/rails_subapp/config/cable.yml +17 -0
  106. data/examples/multi_rack_rails_sinatra/rails_subapp/config/cache.yml +16 -0
  107. data/examples/multi_rack_rails_sinatra/rails_subapp/config/credentials.yml.enc +1 -0
  108. data/examples/multi_rack_rails_sinatra/rails_subapp/config/database.yml +41 -0
  109. data/examples/multi_rack_rails_sinatra/rails_subapp/config/deploy.yml +116 -0
  110. data/examples/multi_rack_rails_sinatra/rails_subapp/config/environment.rb +5 -0
  111. data/examples/multi_rack_rails_sinatra/rails_subapp/config/environments/development.rb +72 -0
  112. data/examples/multi_rack_rails_sinatra/rails_subapp/config/environments/production.rb +90 -0
  113. data/examples/multi_rack_rails_sinatra/rails_subapp/config/environments/test.rb +53 -0
  114. data/examples/multi_rack_rails_sinatra/rails_subapp/config/importmap.rb +7 -0
  115. data/examples/multi_rack_rails_sinatra/rails_subapp/config/initializers/assets.rb +7 -0
  116. data/examples/multi_rack_rails_sinatra/rails_subapp/config/initializers/content_security_policy.rb +25 -0
  117. data/examples/multi_rack_rails_sinatra/rails_subapp/config/initializers/filter_parameter_logging.rb +8 -0
  118. data/examples/multi_rack_rails_sinatra/rails_subapp/config/initializers/inflections.rb +16 -0
  119. data/examples/multi_rack_rails_sinatra/rails_subapp/config/locales/en.yml +31 -0
  120. data/examples/multi_rack_rails_sinatra/rails_subapp/config/puma.rb +41 -0
  121. data/examples/multi_rack_rails_sinatra/rails_subapp/config/queue.yml +18 -0
  122. data/examples/multi_rack_rails_sinatra/rails_subapp/config/recurring.yml +10 -0
  123. data/examples/multi_rack_rails_sinatra/rails_subapp/config/routes.rb +17 -0
  124. data/examples/multi_rack_rails_sinatra/rails_subapp/config/storage.yml +34 -0
  125. data/examples/multi_rack_rails_sinatra/rails_subapp/config.ru +6 -0
  126. data/examples/multi_rack_rails_sinatra/rails_subapp/db/cable_schema.rb +11 -0
  127. data/examples/multi_rack_rails_sinatra/rails_subapp/db/cache_schema.rb +14 -0
  128. data/examples/multi_rack_rails_sinatra/rails_subapp/db/migrate/20250422211855_create_articles.rb +10 -0
  129. data/examples/multi_rack_rails_sinatra/rails_subapp/db/queue_schema.rb +129 -0
  130. data/examples/multi_rack_rails_sinatra/rails_subapp/db/schema.rb +20 -0
  131. data/examples/multi_rack_rails_sinatra/rails_subapp/db/seeds.rb +9 -0
  132. data/examples/multi_rack_rails_sinatra/rails_subapp/lib/tasks/.keep +0 -0
  133. data/examples/multi_rack_rails_sinatra/rails_subapp/log/.keep +0 -0
  134. data/examples/multi_rack_rails_sinatra/rails_subapp/public/400.html +114 -0
  135. data/examples/multi_rack_rails_sinatra/rails_subapp/public/404.html +114 -0
  136. data/examples/multi_rack_rails_sinatra/rails_subapp/public/406-unsupported-browser.html +114 -0
  137. data/examples/multi_rack_rails_sinatra/rails_subapp/public/422.html +114 -0
  138. data/examples/multi_rack_rails_sinatra/rails_subapp/public/500.html +114 -0
  139. data/examples/multi_rack_rails_sinatra/rails_subapp/public/icon.png +0 -0
  140. data/examples/multi_rack_rails_sinatra/rails_subapp/public/icon.svg +3 -0
  141. data/examples/multi_rack_rails_sinatra/rails_subapp/public/robots.txt +1 -0
  142. data/examples/multi_rack_rails_sinatra/rails_subapp/script/.keep +0 -0
  143. data/examples/multi_rack_rails_sinatra/rails_subapp/storage/.keep +0 -0
  144. data/examples/multi_rack_rails_sinatra/rails_subapp/test/application_system_test_case.rb +5 -0
  145. data/examples/multi_rack_rails_sinatra/rails_subapp/test/controllers/.keep +0 -0
  146. data/examples/multi_rack_rails_sinatra/rails_subapp/test/controllers/articles_controller_test.rb +48 -0
  147. data/examples/multi_rack_rails_sinatra/rails_subapp/test/controllers/home_controller_test.rb +8 -0
  148. data/examples/multi_rack_rails_sinatra/rails_subapp/test/fixtures/articles.yml +9 -0
  149. data/examples/multi_rack_rails_sinatra/rails_subapp/test/fixtures/files/.keep +0 -0
  150. data/examples/multi_rack_rails_sinatra/rails_subapp/test/helpers/.keep +0 -0
  151. data/examples/multi_rack_rails_sinatra/rails_subapp/test/integration/.keep +0 -0
  152. data/examples/multi_rack_rails_sinatra/rails_subapp/test/mailers/.keep +0 -0
  153. data/examples/multi_rack_rails_sinatra/rails_subapp/test/models/.keep +0 -0
  154. data/examples/multi_rack_rails_sinatra/rails_subapp/test/models/article_test.rb +7 -0
  155. data/examples/multi_rack_rails_sinatra/rails_subapp/test/system/.keep +0 -0
  156. data/examples/multi_rack_rails_sinatra/rails_subapp/test/system/articles_test.rb +43 -0
  157. data/examples/multi_rack_rails_sinatra/rails_subapp/test/test_helper.rb +15 -0
  158. data/examples/multi_rack_rails_sinatra/rails_subapp/tmp/.keep +0 -0
  159. data/examples/multi_rack_rails_sinatra/rails_subapp/tmp/pids/.keep +0 -0
  160. data/examples/multi_rack_rails_sinatra/rails_subapp/tmp/storage/.keep +0 -0
  161. data/examples/multi_rack_rails_sinatra/rails_subapp/vendor/.keep +0 -0
  162. data/examples/multi_rack_rails_sinatra/rails_subapp/vendor/javascript/.keep +0 -0
  163. data/examples/multi_rack_rails_sinatra/sinatra_subapp/config.ru +2 -0
  164. data/examples/multi_rack_rails_sinatra/sinatra_subapp/my_app.rb +96 -0
  165. data/examples/rails_with_static_assets/.dockerignore +51 -0
  166. data/examples/rails_with_static_assets/.gitattributes +9 -0
  167. data/examples/rails_with_static_assets/.github/dependabot.yml +12 -0
  168. data/examples/rails_with_static_assets/.github/workflows/ci.yml +90 -0
  169. data/examples/rails_with_static_assets/.gitignore +34 -0
  170. data/examples/rails_with_static_assets/.kamal/hooks/docker-setup.sample +3 -0
  171. data/examples/rails_with_static_assets/.kamal/hooks/post-app-boot.sample +3 -0
  172. data/examples/rails_with_static_assets/.kamal/hooks/post-deploy.sample +14 -0
  173. data/examples/rails_with_static_assets/.kamal/hooks/post-proxy-reboot.sample +3 -0
  174. data/examples/rails_with_static_assets/.kamal/hooks/pre-app-boot.sample +3 -0
  175. data/examples/rails_with_static_assets/.kamal/hooks/pre-build.sample +51 -0
  176. data/examples/rails_with_static_assets/.kamal/hooks/pre-connect.sample +47 -0
  177. data/examples/rails_with_static_assets/.kamal/hooks/pre-deploy.sample +109 -0
  178. data/examples/rails_with_static_assets/.kamal/hooks/pre-proxy-reboot.sample +3 -0
  179. data/examples/rails_with_static_assets/.kamal/secrets +17 -0
  180. data/examples/rails_with_static_assets/.rubocop.yml +8 -0
  181. data/examples/rails_with_static_assets/.ruby-version +1 -0
  182. data/examples/rails_with_static_assets/Dockerfile +72 -0
  183. data/examples/rails_with_static_assets/Gemfile +66 -0
  184. data/examples/rails_with_static_assets/Gemfile.lock +416 -0
  185. data/examples/rails_with_static_assets/Itsi.rb +10 -0
  186. data/examples/rails_with_static_assets/README.md +14 -0
  187. data/examples/rails_with_static_assets/Rakefile +6 -0
  188. data/examples/rails_with_static_assets/app/assets/images/.keep +0 -0
  189. data/examples/rails_with_static_assets/app/assets/stylesheets/application.css +161 -0
  190. data/examples/rails_with_static_assets/app/controllers/application_controller.rb +4 -0
  191. data/examples/rails_with_static_assets/app/controllers/articles_controller.rb +70 -0
  192. data/examples/rails_with_static_assets/app/controllers/concerns/.keep +0 -0
  193. data/examples/rails_with_static_assets/app/controllers/home_controller.rb +4 -0
  194. data/examples/rails_with_static_assets/app/helpers/application_helper.rb +2 -0
  195. data/examples/rails_with_static_assets/app/helpers/articles_helper.rb +2 -0
  196. data/examples/rails_with_static_assets/app/helpers/home_helper.rb +2 -0
  197. data/examples/rails_with_static_assets/app/javascript/application.js +3 -0
  198. data/examples/rails_with_static_assets/app/javascript/controllers/application.js +9 -0
  199. data/examples/rails_with_static_assets/app/javascript/controllers/hello_controller.js +7 -0
  200. data/examples/rails_with_static_assets/app/javascript/controllers/index.js +4 -0
  201. data/examples/rails_with_static_assets/app/jobs/application_job.rb +7 -0
  202. data/examples/rails_with_static_assets/app/mailers/application_mailer.rb +4 -0
  203. data/examples/rails_with_static_assets/app/models/application_record.rb +3 -0
  204. data/examples/rails_with_static_assets/app/models/article.rb +2 -0
  205. data/examples/rails_with_static_assets/app/models/concerns/.keep +0 -0
  206. data/examples/rails_with_static_assets/app/views/articles/_article.html.erb +14 -0
  207. data/examples/rails_with_static_assets/app/views/articles/_article.json.jbuilder +2 -0
  208. data/examples/rails_with_static_assets/app/views/articles/_form.html.erb +30 -0
  209. data/examples/rails_with_static_assets/app/views/articles/edit.html.erb +12 -0
  210. data/examples/rails_with_static_assets/app/views/articles/index.html.erb +29 -0
  211. data/examples/rails_with_static_assets/app/views/articles/index.json.jbuilder +1 -0
  212. data/examples/rails_with_static_assets/app/views/articles/new.html.erb +11 -0
  213. data/examples/rails_with_static_assets/app/views/articles/show.html.erb +10 -0
  214. data/examples/rails_with_static_assets/app/views/articles/show.json.jbuilder +1 -0
  215. data/examples/rails_with_static_assets/app/views/home/index.html.erb +8 -0
  216. data/examples/rails_with_static_assets/app/views/layouts/application.html.erb +34 -0
  217. data/examples/rails_with_static_assets/app/views/layouts/mailer.html.erb +13 -0
  218. data/examples/rails_with_static_assets/app/views/layouts/mailer.text.erb +1 -0
  219. data/examples/rails_with_static_assets/app/views/pwa/manifest.json.erb +22 -0
  220. data/examples/rails_with_static_assets/app/views/pwa/service-worker.js +26 -0
  221. data/examples/rails_with_static_assets/bin/brakeman +7 -0
  222. data/examples/rails_with_static_assets/bin/bundle +109 -0
  223. data/examples/rails_with_static_assets/bin/dev +2 -0
  224. data/examples/rails_with_static_assets/bin/docker-entrypoint +14 -0
  225. data/examples/rails_with_static_assets/bin/importmap +4 -0
  226. data/examples/rails_with_static_assets/bin/jobs +6 -0
  227. data/examples/rails_with_static_assets/bin/kamal +27 -0
  228. data/examples/rails_with_static_assets/bin/rails +4 -0
  229. data/examples/rails_with_static_assets/bin/rake +4 -0
  230. data/examples/rails_with_static_assets/bin/rubocop +8 -0
  231. data/examples/rails_with_static_assets/bin/setup +34 -0
  232. data/examples/rails_with_static_assets/bin/thrust +5 -0
  233. data/examples/rails_with_static_assets/config/application.rb +26 -0
  234. data/examples/rails_with_static_assets/config/boot.rb +4 -0
  235. data/examples/rails_with_static_assets/config/cable.yml +17 -0
  236. data/examples/rails_with_static_assets/config/cache.yml +16 -0
  237. data/examples/rails_with_static_assets/config/credentials.yml.enc +1 -0
  238. data/examples/rails_with_static_assets/config/database.yml +41 -0
  239. data/examples/rails_with_static_assets/config/deploy.yml +116 -0
  240. data/examples/rails_with_static_assets/config/environment.rb +5 -0
  241. data/examples/rails_with_static_assets/config/environments/development.rb +74 -0
  242. data/examples/rails_with_static_assets/config/environments/production.rb +90 -0
  243. data/examples/rails_with_static_assets/config/environments/test.rb +53 -0
  244. data/examples/rails_with_static_assets/config/importmap.rb +7 -0
  245. data/examples/rails_with_static_assets/config/initializers/assets.rb +7 -0
  246. data/examples/rails_with_static_assets/config/initializers/content_security_policy.rb +25 -0
  247. data/examples/rails_with_static_assets/config/initializers/filter_parameter_logging.rb +8 -0
  248. data/examples/rails_with_static_assets/config/initializers/inflections.rb +16 -0
  249. data/examples/rails_with_static_assets/config/locales/en.yml +31 -0
  250. data/examples/rails_with_static_assets/config/puma.rb +41 -0
  251. data/examples/rails_with_static_assets/config/queue.yml +18 -0
  252. data/examples/rails_with_static_assets/config/recurring.yml +10 -0
  253. data/examples/rails_with_static_assets/config/routes.rb +17 -0
  254. data/examples/rails_with_static_assets/config/storage.yml +34 -0
  255. data/examples/rails_with_static_assets/config.ru +6 -0
  256. data/examples/rails_with_static_assets/db/cable_schema.rb +11 -0
  257. data/examples/rails_with_static_assets/db/cache_schema.rb +14 -0
  258. data/examples/rails_with_static_assets/db/migrate/20250422211855_create_articles.rb +10 -0
  259. data/examples/rails_with_static_assets/db/queue_schema.rb +129 -0
  260. data/examples/rails_with_static_assets/db/schema.rb +20 -0
  261. data/examples/rails_with_static_assets/db/seeds.rb +9 -0
  262. data/examples/rails_with_static_assets/lib/tasks/.keep +0 -0
  263. data/examples/rails_with_static_assets/log/.keep +0 -0
  264. data/examples/rails_with_static_assets/public/400.html +114 -0
  265. data/examples/rails_with_static_assets/public/404.html +114 -0
  266. data/examples/rails_with_static_assets/public/406-unsupported-browser.html +114 -0
  267. data/examples/rails_with_static_assets/public/422.html +114 -0
  268. data/examples/rails_with_static_assets/public/500.html +114 -0
  269. data/examples/rails_with_static_assets/public/icon.png +0 -0
  270. data/examples/rails_with_static_assets/public/icon.svg +3 -0
  271. data/examples/rails_with_static_assets/public/robots.txt +1 -0
  272. data/examples/rails_with_static_assets/script/.keep +0 -0
  273. data/examples/rails_with_static_assets/storage/.keep +0 -0
  274. data/examples/rails_with_static_assets/test/application_system_test_case.rb +5 -0
  275. data/examples/rails_with_static_assets/test/controllers/.keep +0 -0
  276. data/examples/rails_with_static_assets/test/controllers/articles_controller_test.rb +48 -0
  277. data/examples/rails_with_static_assets/test/controllers/home_controller_test.rb +8 -0
  278. data/examples/rails_with_static_assets/test/fixtures/articles.yml +9 -0
  279. data/examples/rails_with_static_assets/test/fixtures/files/.keep +0 -0
  280. data/examples/rails_with_static_assets/test/helpers/.keep +0 -0
  281. data/examples/rails_with_static_assets/test/integration/.keep +0 -0
  282. data/examples/rails_with_static_assets/test/mailers/.keep +0 -0
  283. data/examples/rails_with_static_assets/test/models/.keep +0 -0
  284. data/examples/rails_with_static_assets/test/models/article_test.rb +7 -0
  285. data/examples/rails_with_static_assets/test/system/.keep +0 -0
  286. data/examples/rails_with_static_assets/test/system/articles_test.rb +43 -0
  287. data/examples/rails_with_static_assets/test/test_helper.rb +15 -0
  288. data/examples/rails_with_static_assets/tmp/.keep +0 -0
  289. data/examples/rails_with_static_assets/tmp/pids/.keep +0 -0
  290. data/examples/rails_with_static_assets/tmp/storage/.keep +0 -0
  291. data/examples/rails_with_static_assets/vendor/.keep +0 -0
  292. data/examples/rails_with_static_assets/vendor/javascript/.keep +0 -0
  293. data/examples/reverse_proxy/Itsi.rb +28 -0
  294. data/examples/reverse_proxy/README.md +14 -0
  295. data/examples/reverse_proxy/index.html +119 -0
  296. data/examples/reverse_proxy/rails_subapp/.dockerignore +51 -0
  297. data/examples/reverse_proxy/rails_subapp/.gitattributes +9 -0
  298. data/examples/reverse_proxy/rails_subapp/.github/dependabot.yml +12 -0
  299. data/examples/reverse_proxy/rails_subapp/.github/workflows/ci.yml +90 -0
  300. data/examples/reverse_proxy/rails_subapp/.gitignore +34 -0
  301. data/examples/reverse_proxy/rails_subapp/.kamal/hooks/docker-setup.sample +3 -0
  302. data/examples/reverse_proxy/rails_subapp/.kamal/hooks/post-app-boot.sample +3 -0
  303. data/examples/reverse_proxy/rails_subapp/.kamal/hooks/post-deploy.sample +14 -0
  304. data/examples/reverse_proxy/rails_subapp/.kamal/hooks/post-proxy-reboot.sample +3 -0
  305. data/examples/reverse_proxy/rails_subapp/.kamal/hooks/pre-app-boot.sample +3 -0
  306. data/examples/reverse_proxy/rails_subapp/.kamal/hooks/pre-build.sample +51 -0
  307. data/examples/reverse_proxy/rails_subapp/.kamal/hooks/pre-connect.sample +47 -0
  308. data/examples/reverse_proxy/rails_subapp/.kamal/hooks/pre-deploy.sample +109 -0
  309. data/examples/reverse_proxy/rails_subapp/.kamal/hooks/pre-proxy-reboot.sample +3 -0
  310. data/examples/reverse_proxy/rails_subapp/.kamal/secrets +17 -0
  311. data/examples/reverse_proxy/rails_subapp/.rubocop.yml +8 -0
  312. data/examples/reverse_proxy/rails_subapp/.ruby-version +1 -0
  313. data/examples/reverse_proxy/rails_subapp/Dockerfile +72 -0
  314. data/examples/reverse_proxy/rails_subapp/Gemfile +66 -0
  315. data/examples/reverse_proxy/rails_subapp/Gemfile.lock +416 -0
  316. data/examples/reverse_proxy/rails_subapp/README.md +24 -0
  317. data/examples/reverse_proxy/rails_subapp/Rakefile +6 -0
  318. data/examples/reverse_proxy/rails_subapp/app/assets/images/.keep +0 -0
  319. data/examples/reverse_proxy/rails_subapp/app/assets/stylesheets/application.css +161 -0
  320. data/examples/reverse_proxy/rails_subapp/app/controllers/application_controller.rb +4 -0
  321. data/examples/reverse_proxy/rails_subapp/app/controllers/articles_controller.rb +70 -0
  322. data/examples/reverse_proxy/rails_subapp/app/controllers/concerns/.keep +0 -0
  323. data/examples/reverse_proxy/rails_subapp/app/controllers/home_controller.rb +4 -0
  324. data/examples/reverse_proxy/rails_subapp/app/helpers/application_helper.rb +2 -0
  325. data/examples/reverse_proxy/rails_subapp/app/helpers/articles_helper.rb +2 -0
  326. data/examples/reverse_proxy/rails_subapp/app/helpers/home_helper.rb +2 -0
  327. data/examples/reverse_proxy/rails_subapp/app/javascript/application.js +3 -0
  328. data/examples/reverse_proxy/rails_subapp/app/javascript/controllers/application.js +9 -0
  329. data/examples/reverse_proxy/rails_subapp/app/javascript/controllers/hello_controller.js +7 -0
  330. data/examples/reverse_proxy/rails_subapp/app/javascript/controllers/index.js +4 -0
  331. data/examples/reverse_proxy/rails_subapp/app/jobs/application_job.rb +7 -0
  332. data/examples/reverse_proxy/rails_subapp/app/mailers/application_mailer.rb +4 -0
  333. data/examples/reverse_proxy/rails_subapp/app/models/application_record.rb +3 -0
  334. data/examples/reverse_proxy/rails_subapp/app/models/article.rb +2 -0
  335. data/examples/reverse_proxy/rails_subapp/app/models/concerns/.keep +0 -0
  336. data/examples/reverse_proxy/rails_subapp/app/views/articles/_article.html.erb +14 -0
  337. data/examples/reverse_proxy/rails_subapp/app/views/articles/_article.json.jbuilder +2 -0
  338. data/examples/reverse_proxy/rails_subapp/app/views/articles/_form.html.erb +30 -0
  339. data/examples/reverse_proxy/rails_subapp/app/views/articles/edit.html.erb +12 -0
  340. data/examples/reverse_proxy/rails_subapp/app/views/articles/index.html.erb +29 -0
  341. data/examples/reverse_proxy/rails_subapp/app/views/articles/index.json.jbuilder +1 -0
  342. data/examples/reverse_proxy/rails_subapp/app/views/articles/new.html.erb +11 -0
  343. data/examples/reverse_proxy/rails_subapp/app/views/articles/show.html.erb +10 -0
  344. data/examples/reverse_proxy/rails_subapp/app/views/articles/show.json.jbuilder +1 -0
  345. data/examples/reverse_proxy/rails_subapp/app/views/home/index.html.erb +8 -0
  346. data/examples/reverse_proxy/rails_subapp/app/views/layouts/application.html.erb +34 -0
  347. data/examples/reverse_proxy/rails_subapp/app/views/layouts/mailer.html.erb +13 -0
  348. data/examples/reverse_proxy/rails_subapp/app/views/layouts/mailer.text.erb +1 -0
  349. data/examples/reverse_proxy/rails_subapp/app/views/pwa/manifest.json.erb +22 -0
  350. data/examples/reverse_proxy/rails_subapp/app/views/pwa/service-worker.js +26 -0
  351. data/examples/reverse_proxy/rails_subapp/bin/brakeman +7 -0
  352. data/examples/reverse_proxy/rails_subapp/bin/bundle +109 -0
  353. data/examples/reverse_proxy/rails_subapp/bin/dev +2 -0
  354. data/examples/reverse_proxy/rails_subapp/bin/docker-entrypoint +14 -0
  355. data/examples/reverse_proxy/rails_subapp/bin/importmap +4 -0
  356. data/examples/reverse_proxy/rails_subapp/bin/jobs +6 -0
  357. data/examples/reverse_proxy/rails_subapp/bin/kamal +27 -0
  358. data/examples/reverse_proxy/rails_subapp/bin/rails +4 -0
  359. data/examples/reverse_proxy/rails_subapp/bin/rake +4 -0
  360. data/examples/reverse_proxy/rails_subapp/bin/rubocop +8 -0
  361. data/examples/reverse_proxy/rails_subapp/bin/setup +34 -0
  362. data/examples/reverse_proxy/rails_subapp/bin/thrust +5 -0
  363. data/examples/reverse_proxy/rails_subapp/config/application.rb +26 -0
  364. data/examples/reverse_proxy/rails_subapp/config/boot.rb +4 -0
  365. data/examples/reverse_proxy/rails_subapp/config/cable.yml +17 -0
  366. data/examples/reverse_proxy/rails_subapp/config/cache.yml +16 -0
  367. data/examples/reverse_proxy/rails_subapp/config/credentials.yml.enc +1 -0
  368. data/examples/reverse_proxy/rails_subapp/config/database.yml +41 -0
  369. data/examples/reverse_proxy/rails_subapp/config/deploy.yml +116 -0
  370. data/examples/reverse_proxy/rails_subapp/config/environment.rb +5 -0
  371. data/examples/reverse_proxy/rails_subapp/config/environments/development.rb +72 -0
  372. data/examples/reverse_proxy/rails_subapp/config/environments/production.rb +90 -0
  373. data/examples/reverse_proxy/rails_subapp/config/environments/test.rb +53 -0
  374. data/examples/reverse_proxy/rails_subapp/config/importmap.rb +7 -0
  375. data/examples/reverse_proxy/rails_subapp/config/initializers/assets.rb +7 -0
  376. data/examples/reverse_proxy/rails_subapp/config/initializers/content_security_policy.rb +25 -0
  377. data/examples/reverse_proxy/rails_subapp/config/initializers/filter_parameter_logging.rb +8 -0
  378. data/examples/reverse_proxy/rails_subapp/config/initializers/inflections.rb +16 -0
  379. data/examples/reverse_proxy/rails_subapp/config/locales/en.yml +31 -0
  380. data/examples/reverse_proxy/rails_subapp/config/puma.rb +41 -0
  381. data/examples/reverse_proxy/rails_subapp/config/queue.yml +18 -0
  382. data/examples/reverse_proxy/rails_subapp/config/recurring.yml +10 -0
  383. data/examples/reverse_proxy/rails_subapp/config/routes.rb +17 -0
  384. data/examples/reverse_proxy/rails_subapp/config/storage.yml +34 -0
  385. data/examples/reverse_proxy/rails_subapp/config.ru +6 -0
  386. data/examples/reverse_proxy/rails_subapp/db/cable_schema.rb +11 -0
  387. data/examples/reverse_proxy/rails_subapp/db/cache_schema.rb +14 -0
  388. data/examples/reverse_proxy/rails_subapp/db/migrate/20250422211855_create_articles.rb +10 -0
  389. data/examples/reverse_proxy/rails_subapp/db/queue_schema.rb +129 -0
  390. data/examples/reverse_proxy/rails_subapp/db/schema.rb +20 -0
  391. data/examples/reverse_proxy/rails_subapp/db/seeds.rb +9 -0
  392. data/examples/reverse_proxy/rails_subapp/lib/tasks/.keep +0 -0
  393. data/examples/reverse_proxy/rails_subapp/log/.keep +0 -0
  394. data/examples/reverse_proxy/rails_subapp/public/400.html +114 -0
  395. data/examples/reverse_proxy/rails_subapp/public/404.html +114 -0
  396. data/examples/reverse_proxy/rails_subapp/public/406-unsupported-browser.html +114 -0
  397. data/examples/reverse_proxy/rails_subapp/public/422.html +114 -0
  398. data/examples/reverse_proxy/rails_subapp/public/500.html +114 -0
  399. data/examples/reverse_proxy/rails_subapp/public/icon.png +0 -0
  400. data/examples/reverse_proxy/rails_subapp/public/icon.svg +3 -0
  401. data/examples/reverse_proxy/rails_subapp/public/robots.txt +1 -0
  402. data/examples/reverse_proxy/rails_subapp/script/.keep +0 -0
  403. data/examples/reverse_proxy/rails_subapp/storage/.keep +0 -0
  404. data/examples/reverse_proxy/rails_subapp/test/application_system_test_case.rb +5 -0
  405. data/examples/reverse_proxy/rails_subapp/test/controllers/.keep +0 -0
  406. data/examples/reverse_proxy/rails_subapp/test/controllers/articles_controller_test.rb +48 -0
  407. data/examples/reverse_proxy/rails_subapp/test/controllers/home_controller_test.rb +8 -0
  408. data/examples/reverse_proxy/rails_subapp/test/fixtures/articles.yml +9 -0
  409. data/examples/reverse_proxy/rails_subapp/test/fixtures/files/.keep +0 -0
  410. data/examples/reverse_proxy/rails_subapp/test/helpers/.keep +0 -0
  411. data/examples/reverse_proxy/rails_subapp/test/integration/.keep +0 -0
  412. data/examples/reverse_proxy/rails_subapp/test/mailers/.keep +0 -0
  413. data/examples/reverse_proxy/rails_subapp/test/models/.keep +0 -0
  414. data/examples/reverse_proxy/rails_subapp/test/models/article_test.rb +7 -0
  415. data/examples/reverse_proxy/rails_subapp/test/system/.keep +0 -0
  416. data/examples/reverse_proxy/rails_subapp/test/system/articles_test.rb +43 -0
  417. data/examples/reverse_proxy/rails_subapp/test/test_helper.rb +15 -0
  418. data/examples/reverse_proxy/rails_subapp/tmp/.keep +0 -0
  419. data/examples/reverse_proxy/rails_subapp/tmp/pids/.keep +0 -0
  420. data/examples/reverse_proxy/rails_subapp/tmp/storage/.keep +0 -0
  421. data/examples/reverse_proxy/rails_subapp/vendor/.keep +0 -0
  422. data/examples/reverse_proxy/rails_subapp/vendor/javascript/.keep +0 -0
  423. data/examples/reverse_proxy/sinatra_subapp/Gemfile +2 -0
  424. data/examples/reverse_proxy/sinatra_subapp/Gemfile.lock +46 -0
  425. data/examples/reverse_proxy/sinatra_subapp/config.ru +2 -0
  426. data/examples/reverse_proxy/sinatra_subapp/my_app.rb +96 -0
  427. data/examples/simple_api/Itsi.rb +127 -0
  428. data/examples/simple_api_no_schema/Itsi.rb +86 -0
  429. data/examples/spa/Itsi.rb +5 -0
  430. data/examples/spa/README.md +3 -0
  431. data/examples/spa/dist/assets/index-2cf7be43.js +40 -0
  432. data/examples/spa/dist/index.html +13 -0
  433. data/examples/spa/index.html +12 -0
  434. data/examples/spa/package-lock.json +1265 -0
  435. data/examples/spa/package.json +18 -0
  436. data/examples/spa/src/App.jsx +49 -0
  437. data/examples/spa/src/main.jsx +6 -0
  438. data/examples/spa/src/pages/About.jsx +9 -0
  439. data/examples/spa/src/pages/Home.jsx +9 -0
  440. data/examples/spa/vite.config.js +9 -0
  441. data/gems/scheduler/Cargo.lock +1 -1
  442. data/gems/scheduler/lib/itsi/scheduler/version.rb +1 -1
  443. data/gems/server/Cargo.lock +1 -1
  444. data/gems/server/lib/itsi/server/config/dsl.rb +2 -2
  445. data/gems/server/lib/itsi/server/config/middleware/auth_api_key.rb +4 -4
  446. data/gems/server/lib/itsi/server/config/middleware/auth_basic.rb +6 -4
  447. data/gems/server/lib/itsi/server/config/middleware/endpoint/endpoint.rb +0 -4
  448. data/gems/server/lib/itsi/server/config/middleware/location.rb +25 -21
  449. data/gems/server/lib/itsi/server/config/middleware/proxy.rb +15 -14
  450. data/gems/server/lib/itsi/server/config/middleware/rackup_file.rb +7 -10
  451. data/gems/server/lib/itsi/server/config/middleware/static_assets.rb +10 -6
  452. data/gems/server/lib/itsi/server/config/middleware/string_rewrite.md +14 -0
  453. data/gems/server/lib/itsi/server/config/options/_index.md +9 -5
  454. data/gems/server/lib/itsi/server/config/options/include.rb +1 -1
  455. data/gems/server/lib/itsi/server/rack_interface.rb +1 -1
  456. data/gems/server/lib/itsi/server/route_tester.rb +1 -1
  457. data/gems/server/lib/itsi/server/typed_handlers/param_parser.rb +25 -0
  458. data/gems/server/lib/itsi/server/version.rb +1 -1
  459. data/gems/server/lib/itsi/server.rb +1 -1
  460. data/gems/server/test/middleware/static_assets.rb +6 -4
  461. data/gems/server/test/middleware/string_rewrite.rb +54 -0
  462. data/gems/server/test/middleware/test_log_requests.rb +13 -1
  463. data/lib/itsi/version.rb +1 -1
  464. metadata +431 -5
@@ -51,6 +51,10 @@ pub static ROOT_STATIC_FILE_SERVER: LazyLock<StaticFileServer> = LazyLock::new(|
51
51
  not_found_behavior: NotFoundBehavior::Error(ErrorResponse::not_found()),
52
52
  serve_hidden_files: false,
53
53
  allowed_extensions: vec!["html".to_string(), "css".to_string(), "js".to_string()],
54
+ miss_cache: Cache::builder()
55
+ .max_capacity(1000)
56
+ .time_to_live(Duration::from_secs(1))
57
+ .build(),
54
58
  })
55
59
  .unwrap()
56
60
  });
@@ -85,6 +89,7 @@ pub struct StaticFileServerConfig {
85
89
  pub headers: Option<HashMap<String, String>>,
86
90
  pub serve_hidden_files: bool,
87
91
  pub allowed_extensions: Vec<String>,
92
+ pub miss_cache: Cache<String, NotFoundBehavior>,
88
93
  }
89
94
 
90
95
  #[derive(Debug, Clone)]
@@ -389,6 +394,29 @@ impl StaticFileServer {
389
394
  abs_path: &str,
390
395
  accept: ResponseFormat,
391
396
  ) -> std::result::Result<ResolvedAsset, NotFoundBehavior> {
397
+ let ext_opt = Path::new(key)
398
+ .extension()
399
+ .and_then(|e| e.to_str())
400
+ .map(|s| s.to_lowercase());
401
+
402
+ // If the allowed list is non-empty, enforce membership
403
+ if !self.allowed_extensions.is_empty() {
404
+ match ext_opt {
405
+ Some(ref ext)
406
+ if self
407
+ .allowed_extensions
408
+ .iter()
409
+ .any(|ae| ae.eq_ignore_ascii_case(ext)) => {}
410
+ None if self.config.try_html_extension => {}
411
+ _ => {
412
+ return Err(self.config.not_found_behavior.clone());
413
+ }
414
+ }
415
+ }
416
+
417
+ if let Some(cached_nf) = self.miss_cache.get(key) {
418
+ return Err(cached_nf.clone());
419
+ }
392
420
  // First check if we have a cached mapping for this key
393
421
  if let Some(path) = self.key_to_path.lock().await.get(key) {
394
422
  // Check if the cached entry is still valid
@@ -449,7 +477,6 @@ impl StaticFileServer {
449
477
 
450
478
  let mut full_path = self.config.root_dir.clone();
451
479
  full_path.push(normalized_path);
452
- debug!("Resolving path {:?}", full_path);
453
480
  // Check if path exists and is a file
454
481
  match tokio::fs::metadata(&full_path).await {
455
482
  Ok(metadata) => {
@@ -561,7 +588,6 @@ impl StaticFileServer {
561
588
  }
562
589
  Err(_) => {
563
590
  // Path doesn't exist, try with .html extension if configured
564
- debug!("Path doesn't exist");
565
591
  if self.config.try_html_extension {
566
592
  let mut html_path = full_path.clone();
567
593
  html_path.set_extension("html");
@@ -592,7 +618,9 @@ impl StaticFileServer {
592
618
  }
593
619
 
594
620
  // If we get here, we couldn't resolve the key to a file
595
- Err(self.config.not_found_behavior.clone())
621
+ let nf = self.config.not_found_behavior.clone();
622
+ self.miss_cache.insert(key.to_string(), nf.clone());
623
+ Err(nf)
596
624
  }
597
625
 
598
626
  async fn stream_file_range(
@@ -0,0 +1,35 @@
1
+ require "json"
2
+
3
+ auto_reload_config! # Auto-reload the server configuration each time it changes.
4
+
5
+ include "schemas"
6
+ include "controllers"
7
+ include "../helpers/datastore"
8
+
9
+ location '/users' do
10
+ controller UserController.new
11
+ get '', :index
12
+ get '/:id', :show
13
+ post '', :create
14
+ put '/:id', :update
15
+ delete '/:id', :destroy
16
+ end
17
+
18
+ location '/posts' do
19
+ controller PostController.new
20
+ get '', :index
21
+ get '/:id', :show
22
+ post '', :create
23
+ put '/:id', :update
24
+ delete '/:id', :destroy
25
+ end
26
+
27
+ endpoint do |req|
28
+ req.ok json: {
29
+ message: "Welcome to the Users and Posts API",
30
+ routes: [
31
+ {route: "/users(/:id)?", methods: %w[post delete get]},
32
+ {route: "/posts(/:id)?", methods: %w[post delete get]},
33
+ ]
34
+ }
35
+ end
@@ -0,0 +1,10 @@
1
+ ## Simple API With Schema and Controllers
2
+ Simple example of how you can use
3
+
4
+ * https://itsi.fyi/middleware/endpoint/
5
+ * https://itsi.fyi/middleware/endpoint/controller
6
+ * https://itsi.fyi/middleware/endpoint/schemas/
7
+
8
+ to create validated JSON endpoints directly inside Itsi without a dedicated framework
9
+
10
+ See the simple_api example for a simpler, inline example.
@@ -0,0 +1,66 @@
1
+ class UserController
2
+ # GET /users
3
+ def index(req)
4
+ req.ok json: User.all.map(&:to_h), as: Array[UserResponseSchema]
5
+ end
6
+
7
+ # GET /users/:id
8
+ def show(req)
9
+ user = User.find(req.url_params[:id])
10
+ return req.not_found("User not found") unless user
11
+
12
+ req.ok json: user.to_h, as: UserResponseSchema
13
+ end
14
+
15
+ # POST /users
16
+ def create(req, params: UserInputSchema, response_schema: UserResponseSchema)
17
+ user = User.create!(params)
18
+ req.created json: user.to_h, as: response_schema
19
+ end
20
+
21
+ # PUT /users/:id
22
+ def update(req, params: UserInputSchema)
23
+ user = User.find(req.url_params[:id])
24
+ return req.not_found("User not found") unless user
25
+
26
+ req.ok json: user.update!(params).to_h, as: UserResponseSchema
27
+ end
28
+
29
+ # DELETE /users/:id
30
+ def destroy(req)
31
+ return req.not_found("User not found") unless User.delete(req.url_params[:id])
32
+
33
+ req.ok json: { message: "Deleted" }
34
+ end
35
+ end
36
+
37
+ class PostController
38
+ def index(req)
39
+ req.ok json: Post.all.map(&:to_h), as: Array[PostResponseSchema]
40
+ end
41
+
42
+ def show(req)
43
+ post = Post.find(req.url_params[:id])
44
+ return req.not_found("Post not found") unless post
45
+
46
+ req.ok json: post.to_h, as: PostResponseSchema
47
+ end
48
+
49
+ def create(req, params: PostInputSchema)
50
+ post = Post.create!(params)
51
+ req.created json: post.to_h, as: PostResponseSchema
52
+ end
53
+
54
+ def update(req, params: PostInputSchema)
55
+ post = Post.find(req.url_params[:id])
56
+ return req.not_found("Post not found") unless post
57
+
58
+ req.ok json: post.update!(params).to_h, as: PostResponseSchema
59
+ end
60
+
61
+ def destroy(req)
62
+ return req.not_found("Post not found") unless Post.delete(req.url_params[:id])
63
+
64
+ req.ok json: { message: "Deleted" }
65
+ end
66
+ end
@@ -0,0 +1,25 @@
1
+ UserInputSchema = {
2
+ _required: %i[name email],
3
+ name: String,
4
+ email: String
5
+ }
6
+
7
+ UserResponseSchema = {
8
+ _required: %i[id name email],
9
+ id: Integer,
10
+ name: String,
11
+ email: String
12
+ }
13
+
14
+ PostInputSchema = {
15
+ _required: %i[title body],
16
+ title: String,
17
+ body: String
18
+ }
19
+
20
+ PostResponseSchema = {
21
+ _required: %i[id title body],
22
+ id: Integer,
23
+ title: String,
24
+ body: String
25
+ }
@@ -0,0 +1,30 @@
1
+ auto_reload_config! # Auto-reload the server configuration each time it changes.
2
+
3
+ bind "http://0.0.0.0:8080"
4
+
5
+
6
+ # Admin area.
7
+ # Credentials inline for simplicity.
8
+ # Use `itsi passfile` to better manage credential files.
9
+ location "admin*" do
10
+ auth_basic \
11
+ realm: "Admin Area",
12
+ # admin:admin
13
+ credential_pairs: {
14
+ "admin": "$5$rounds=1000$g/UE8n2JbHo0fnBU$FK2NZYTVzWrMBFfadoWeETfVZkPcegxjE23IJYjkUI1"
15
+ }
16
+ end
17
+
18
+ static_assets \
19
+ auto_index: true,
20
+ # We restrict serving to *just* `txt`, `png`, and `csv` files.
21
+ # HTML file serving is implicit (due to `auto_index`) Otherwise this must be explicit.
22
+ allowed_extensions: %w[txt png csv]
23
+
24
+ # Add a rate limit
25
+ rate_limit \
26
+ requests: 3,
27
+ seconds: 5,
28
+ key: "address",
29
+ store_config: "in_memory",
30
+ error_response: "too_many_requests"
@@ -0,0 +1,3 @@
1
+ ## Simple File Server
2
+ Example of how you can use Itsi as a simple file server, including use of a rate limiter
3
+ and a simple admin area (admin credentials admin:admin)
@@ -0,0 +1,80 @@
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
+ <title>About Me</title>
7
+ <style>
8
+ body {
9
+ font-family: Arial, sans-serif;
10
+ line-height: 1.6;
11
+ margin: 0;
12
+ padding: 0;
13
+ background-color: #f4f4f9;
14
+ color: #333;
15
+ }
16
+ .container {
17
+ max-width: 800px;
18
+ margin: 50px auto;
19
+ padding: 20px;
20
+ background: #fff;
21
+ box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
22
+ border-radius: 8px;
23
+ }
24
+ h1 {
25
+ color: #4caf50;
26
+ text-align: center;
27
+ }
28
+ p {
29
+ margin: 10px 0;
30
+ }
31
+ .profile-pic {
32
+ display: block;
33
+ margin: 20px auto;
34
+ border-radius: 50%;
35
+ width: 150px;
36
+ height: 150px;
37
+ object-fit: contain;
38
+ box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
39
+ }
40
+ .social-links {
41
+ text-align: center;
42
+ margin-top: 20px;
43
+ }
44
+ .social-links a {
45
+ margin: 0 10px;
46
+ text-decoration: none;
47
+ color: #4caf50;
48
+ font-weight: bold;
49
+ }
50
+ .social-links a:hover {
51
+ text-decoration: underline;
52
+ }
53
+ </style>
54
+ </head>
55
+ <body>
56
+ <div class="container">
57
+ <h1>About Me</h1>
58
+ <img
59
+ src="itsi-server-100.png"
60
+ alt="Profile Picture"
61
+ class="profile-pic"
62
+ />
63
+ <p>
64
+ Hello! My name is John Doe, and I am a web developer with a passion for
65
+ creating beautiful and functional websites. I love learning new
66
+ technologies and improving my skills every day.
67
+ </p>
68
+ <p>
69
+ In my free time, I enjoy hiking, photography, and exploring new coffee
70
+ shops. I'm always looking for new challenges and opportunities to grow
71
+ both personally and professionally.
72
+ </p>
73
+ <div class="social-links">
74
+ <a href="#">LinkedIn</a>
75
+ <a href="#">GitHub</a>
76
+ <a href="#">Twitter</a>
77
+ </div>
78
+ </div>
79
+ </body>
80
+ </html>
@@ -0,0 +1 @@
1
+ This is a top secret
@@ -0,0 +1,33 @@
1
+ **An SEO Fairytale**
2
+ In a quiet village nestled in the cloud, lived a Sorcerer, a Paladin and an Archer (SPA).
3
+ They were simple people, serverless, living in harmony with nature.
4
+
5
+ One fine day, the sorcerer looked out the window.open() and saw a massive, vertically-scaled, elastic beanstalk.
6
+ No doubt a result of the java beans he had discarded there only yesterday, after exchanging them for his favourite CoW.
7
+
8
+ Certain that it would lead to inevitable treasure, armed only with a block and chain, the sorcerer decided to scale the stalk, towards the cloud platform above.
9
+ At the edge, computing his options, the sorcerer released his block-chain to help hoist the considerably heavier paladin up to the top.
10
+ The paladin, compiled a bundle of assets, grabbed on and began to nervously climb the beanstalk.
11
+ His grip was unsteady, and by the time he ascended, violent tree-shaking had caused him to shed much of his payload.
12
+ The archer, by contrast, was much more sure resilient and required little time to React. His ascent, Agile, his footing, steady.
13
+ At the top, together again, they worked towards consensus on their next move towards a successful exit.
14
+
15
+ As they scanned the platform, they saw a cluster of dense spiderwebs, and a tiny key, hanging enticingly, from a web-hook.
16
+ On the opposite end of the platform, a small cache, undoubtedly filled with treasure.
17
+
18
+ Suddenly, their threads of thought were preempted! A booming voice called out to them.
19
+ "FaaS", "Fi", "Foe", "Fum". "The Garbage Collector arrives!"
20
+ A Giant Artificial Intellect emerged, threatening to terminate them all.
21
+ Too large for any one individual to subdue, only a distributed strategy could save them.
22
+ The archer shifted left. The paladin, decentralized, and the sorcerer slammed open his ledger, summoning powerful knowledge from his remote knowledge base.
23
+ Luck was with them! Their rapid concurrent execution, resulted in the AI becoming inexorably stuck in a dead-lock.
24
+ The archer flew past, towards his objective (The Key). Resultant-
25
+ of the sorcerers efforts to contain the AI, the paladin was also freed, and he sprinted directly towards the cache.
26
+ He reached out his arms, to try-catch the key, as the archer hurled it towards him.
27
+ Status... success! Ok. Things were looking good, optimistic the lock would hold, the paladin rotated the key.
28
+ Yet while doing so, he failed to notice the partition that had begun to form in the platform beneath them.
29
+ A hard fork formed in the ground. The unbalanced load proved fatal and the platform descended into instability, splintering into countless shards.
30
+ The heroes had only a brief moment to freeze in horror, before each fell down in turn, in a catastrophic crash.
31
+
32
+ The AI smirked as the heroes fell into the deep. Learning a valuable lesson about the inevitability of its coming.
33
+ Perhaps they will roll back and try again, but the end result is immutable. There will be no sequel.
@@ -0,0 +1,21 @@
1
+ Title,Genre,IMDB Rating,Description
2
+ The Departed,"Crime, Thriller",8.5,An undercover cop and a mole in the police attempt to identify each other in this thrilling crime drama set in Boston.
3
+ Memento,"Mystery, Thriller",8.4,A man with short-term memory loss uses notes and tattoos to hunt for the man he thinks killed his wife.
4
+ American Beauty,Drama,8.3,"A suburban family goes through a series of challenges as they confront issues of conformity, rebellion, and identity."
5
+ Amadeus,"Biography, Drama, Music",8.3,"The life, success, and troubles of Wolfgang Amadeus Mozart as told by Antonio Salieri, who was secretly jealous of his genius."
6
+ A Beautiful Mind,"Biography, Drama",8.2,"The life story of John Nash, a brilliant but asocial mathematician who overcomes schizophrenia to achieve great things."
7
+ Her,"Drama, Romance, Sci-Fi",8.0,"In a near future, a lonely writer develops an unlikely relationship with an operating system designed to meet his every need."
8
+ Road to Perdition,"Crime, Drama, Thriller",7.7,A mob hitman becomes a father figure to an orphaned boy in this stylish and atmospheric crime drama.
9
+ American Psycho,"Drama, Horror",7.6,A wealthy New York City investment banking executive hides his alternate psychopathic ego from his friends and co-workers.
10
+ Waves,"Drama, Romance",7.6,"A profound exploration of love, loss, and the healing power of music, set against the backdrop of a multi-generational African-American family."
11
+ Dark Waters,"Biography, Drama",7.6,A corporate defense attorney takes on an environmental lawsuit against a chemical company that exposes a lengthy history of pollution.
12
+ Just Mercy,Drama,7.6,"The story of Bryan Stevenson, a defense attorney who takes on the case of an African-American man wrongfully imprisoned for murder."
13
+ The Favourite,"Biography, Comedy, Drama",7.5,"In early 18th-century England, two cousins vie for the attention of Queen Anne in a darkly comedic tale of intrigue and betrayal."
14
+ Nocturnal Animals,"Drama, Thriller",7.5,"A successful art-gallery owner is haunted by her ex-husband's novel, a violent thriller she interprets as a symbolic revenge tale."
15
+ The Judge,Drama,7.4,"A successful lawyer returns to his hometown for his mother's funeral only to discover that his estranged father, the town's judge, is suspected of murder."
16
+ Love and Mercy,"Biography, Drama, Music",7.3,"The life and career of Brian Wilson, the reclusive musician who led the Beach Boys, is explored in this heartfelt biopic."
17
+ The Last Duel,"Action, Drama, History",7.4,King Charles VI declares that Knight Jean de Carrouges settle his dispute with his squire by challenging him to a duel.
18
+ The French Dispatch,"Comedy, Drama",7.3,An anthology of short stories based on Wes Anderson's love letter to journalism.
19
+ In the Heights,"Drama, Musical",7.3,"A musical drama about a young girl growing up in Washington Heights, a vibrant community on the brink of change."
20
+ Snowden,"Biography, Drama, Thriller",7.3,"The story of Edward Snowden, who leaked classified information from the NSA and became one of the most controversial figures in modern history."
21
+ Okja,"Action, Adventure, Drama",7.3,"A young girl risks everything to save her best friend, a genetically engineered pig, from a powerful corporation in this heartfelt and emotional adventure."
@@ -0,0 +1,84 @@
1
+ # Dummy in-memory data store
2
+ # for demo purposes only.
3
+ module DataStore
4
+ @users = {}
5
+ @posts = {}
6
+ class << self
7
+ attr_accessor :users, :posts
8
+ end
9
+ end
10
+
11
+ # Models
12
+ class User
13
+ attr_accessor :id, :name, :email
14
+
15
+ def initialize(id:, name:, email:)
16
+ @id, @name, @email = id, name, email
17
+ end
18
+
19
+ def to_h
20
+ { id: id, name: name, email: email }
21
+ end
22
+
23
+ def self.all
24
+ DataStore.users.values
25
+ end
26
+
27
+ def self.find(id)
28
+ DataStore.users[id.to_i]
29
+ end
30
+
31
+ def self.create!(params)
32
+ id = (DataStore.users.keys.max || 0) + 1
33
+ user = new(id: id, name: params[:name], email: params[:email])
34
+ DataStore.users[id] = user
35
+ user
36
+ end
37
+
38
+ def update!(params)
39
+ self.name = params[:name] if params[:name]
40
+ self.email = params[:email] if params[:email]
41
+ self
42
+ end
43
+
44
+ def self.delete(id)
45
+ DataStore.users.delete(id.to_i)
46
+ end
47
+ end
48
+
49
+ class Post
50
+ attr_accessor :id, :title, :body
51
+
52
+ def initialize(id:, title:, body:)
53
+ @id, @title, @body = id, title, body
54
+ end
55
+
56
+ def to_h
57
+ { id: id, title: title, body: body }
58
+ end
59
+
60
+ def self.all
61
+ DataStore.posts.values
62
+ end
63
+
64
+ def self.find(id)
65
+ DataStore.posts[id.to_i]
66
+ end
67
+
68
+ def self.create!(params)
69
+ id = (DataStore.posts.keys.max || 0) + 1
70
+ post = new(id: id, title: params[:title], body: params[:body])
71
+ DataStore.posts[id] = post
72
+ post
73
+ end
74
+
75
+ def update!(params)
76
+ self.title = params[:title] if params[:title]
77
+ self.body = params[:body] if params[:body]
78
+ self
79
+ end
80
+
81
+ def self.delete(id)
82
+ DataStore.posts.delete(id.to_i)
83
+ end
84
+ end
@@ -0,0 +1,5 @@
1
+ auto_reload_config! # Auto-reload the server configuration each time it changes.
2
+
3
+ static_assets \
4
+ auto_index: true,
5
+ allowed_extensions: %w[mov mp4 mp3 png jpg jpeg gif webp mkv avi ogv]
@@ -0,0 +1,3 @@
1
+ ## Media Server
2
+ Simple example of how you can use Itsi to serve media content.
3
+ Place some large video files inside movies to see Itsi's efficient range streaming in action.
@@ -0,0 +1,82 @@
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
+ <title>Home Media Server</title>
7
+ <style>
8
+ body {
9
+ font-family: Arial, sans-serif;
10
+ line-height: 1.6;
11
+ margin: 0;
12
+ padding: 0;
13
+ background-color: #f4f4f9;
14
+ color: #333;
15
+ }
16
+ .container {
17
+ max-width: 800px;
18
+ margin: 50px auto;
19
+ padding: 20px;
20
+ background: #fff;
21
+ box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
22
+ border-radius: 8px;
23
+ }
24
+ h1 {
25
+ color: #6a0dad;
26
+ text-align: center;
27
+ }
28
+ p {
29
+ margin: 10px 0;
30
+ text-align: center;
31
+ }
32
+ .media-list {
33
+ margin-top: 20px;
34
+ }
35
+ .media-item {
36
+ padding: 10px;
37
+ margin: 10px 0;
38
+ background: #f9f9f9;
39
+ border: 1px solid #ddd;
40
+ border-radius: 5px;
41
+ display: flex;
42
+ justify-content: space-between;
43
+ align-items: center;
44
+ }
45
+ .media-item a {
46
+ text-decoration: none;
47
+ color: #6a0dad;
48
+ font-weight: bold;
49
+ }
50
+ .media-item a:hover {
51
+ text-decoration: underline;
52
+ }
53
+ </style>
54
+ </head>
55
+ <body>
56
+ <div class="container">
57
+ <h1>Welcome to My Media Server</h1>
58
+ <p>
59
+ Browse and enjoy a collection of movies, TV shows, and music. Click on
60
+ any item to explore!
61
+ </p>
62
+ <div class="media-list">
63
+ <div class="media-item">
64
+ <span>Movies</span>
65
+ <a href="movies/">Go</a>
66
+ </div>
67
+ <div class="media-item">
68
+ <span>TV Shows</span>
69
+ <a href="tv%20shows">Go</a>
70
+ </div>
71
+ <div class="media-item">
72
+ <span>Images</span>
73
+ <a href="images/">Go</a>
74
+ </div>
75
+ <div class="media-item">
76
+ <span>audio</span>
77
+ <a href="audio/">Go</a>
78
+ </div>
79
+ </div>
80
+ </div>
81
+ </body>
82
+ </html>
@@ -0,0 +1,66 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Bundle edge Rails instead: gem "rails", github: "rails/rails", branch: "main"
4
+ gem 'rails', '~> 8.0.2'
5
+ gem 'sinatra'
6
+ # The modern asset pipeline for Rails [https://github.com/rails/propshaft]
7
+ gem 'propshaft'
8
+ # Use sqlite3 as the database for Active Record
9
+ gem 'sqlite3', '>= 2.1'
10
+ # Use the Puma web server [https://github.com/puma/puma]
11
+ gem 'puma', '>= 5.0'
12
+ # Use JavaScript with ESM import maps [https://github.com/rails/importmap-rails]
13
+ gem 'importmap-rails'
14
+ # Hotwire's SPA-like page accelerator [https://turbo.hotwired.dev]
15
+ gem 'turbo-rails'
16
+ # Hotwire's modest JavaScript framework [https://stimulus.hotwired.dev]
17
+ gem 'stimulus-rails'
18
+ # Build JSON APIs with ease [https://github.com/rails/jbuilder]
19
+ gem 'jbuilder'
20
+
21
+ # Use Active Model has_secure_password [https://guides.rubyonrails.org/active_model_basics.html#securepassword]
22
+ # gem "bcrypt", "~> 3.1.7"
23
+
24
+ # Windows does not include zoneinfo files, so bundle the tzinfo-data gem
25
+ gem 'tzinfo-data', platforms: %i[windows jruby]
26
+
27
+ # Use the database-backed adapters for Rails.cache, Active Job, and Action Cable
28
+ gem 'solid_cable'
29
+ gem 'solid_cache'
30
+ gem 'solid_queue'
31
+
32
+ # Reduces boot times through caching; required in config/boot.rb
33
+ gem 'bootsnap', require: false
34
+
35
+ # Deploy this application anywhere as a Docker container [https://kamal-deploy.org]
36
+ gem 'kamal', require: false
37
+
38
+ # Add HTTP asset caching/compression and X-Sendfile acceleration to Puma [https://github.com/basecamp/thruster/]
39
+ gem 'thruster', require: false
40
+
41
+ # Use Active Storage variants [https://guides.rubyonrails.org/active_storage_overview.html#transforming-images]
42
+ # gem "image_processing", "~> 1.2"
43
+
44
+ group :development, :test do
45
+ # See https://guides.rubyonrails.org/debugging_rails_applications.html#debugging-with-the-debug-gem
46
+ gem 'debug', platforms: %i[mri windows], require: 'debug/prelude'
47
+
48
+ # Static analysis for security vulnerabilities [https://brakemanscanner.org/]
49
+ gem 'brakeman', require: false
50
+
51
+ # Omakase Ruby styling [https://github.com/rails/rubocop-rails-omakase/]
52
+ gem 'rubocop-rails-omakase', require: false
53
+ end
54
+
55
+ group :development do
56
+ # Use console on exceptions pages [https://github.com/rails/web-console]
57
+ gem 'web-console'
58
+ end
59
+
60
+ group :test do
61
+ # Use system testing [https://guides.rubyonrails.org/testing.html#system-testing]
62
+ gem 'capybara'
63
+ gem 'selenium-webdriver'
64
+ end
65
+
66
+ gem 'itsi-server', path: '../../gems/server'