├── .env.sample ├── .github ├── dependabot.yml └── workflows │ ├── ci.yml │ ├── release.yml │ └── weekly.yml ├── .gitignore ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── Earthfile ├── LICENSE ├── README.md ├── docker-compose.yml ├── fixtures ├── api_authentication_template │ └── expected │ │ ├── spec │ │ └── requests │ │ │ └── api │ │ │ ├── me │ │ │ └── show_spec.cr │ │ │ ├── sign_ins │ │ │ └── create_spec.cr │ │ │ └── sign_ups │ │ │ └── create_spec.cr │ │ └── src │ │ ├── actions │ │ ├── api │ │ │ ├── me │ │ │ │ └── show.cr │ │ │ ├── sign_ins │ │ │ │ └── create.cr │ │ │ └── sign_ups │ │ │ │ └── create.cr │ │ └── mixins │ │ │ └── api │ │ │ └── auth │ │ │ ├── helpers.cr │ │ │ ├── require_auth_token.cr │ │ │ └── skip_require_auth_token.cr │ │ ├── models │ │ └── user_token.cr │ │ └── serializers │ │ └── user_serializer.cr ├── app_sec_tester_template │ └── expected │ │ └── spec │ │ ├── flows │ │ └── security_spec.cr │ │ └── setup │ │ └── sec_tester.cr ├── app_sec_tester_template__browser │ └── expected │ │ └── spec │ │ ├── flows │ │ └── security_spec.cr │ │ └── setup │ │ └── sec_tester.cr ├── app_sec_tester_template__generate_auth │ └── expected │ │ └── spec │ │ ├── flows │ │ └── security_spec.cr │ │ └── setup │ │ └── sec_tester.cr ├── app_sec_tester_template__no_browser_auth │ └── expected │ │ └── spec │ │ ├── flows │ │ └── security_spec.cr │ │ └── setup │ │ └── sec_tester.cr ├── base_authentication_src_template │ └── expected │ │ ├── config │ │ └── authentic.cr │ │ ├── db │ │ └── migrations │ │ │ ├── .keep │ │ │ └── 00000000000001_create_users.cr │ │ ├── spec │ │ └── support │ │ │ ├── .keep │ │ │ └── factories │ │ │ └── user_factory.cr │ │ └── src │ │ ├── models │ │ └── user.cr │ │ ├── operations │ │ ├── .keep │ │ ├── mixins │ │ │ ├── .keep │ │ │ ├── password_validations.cr │ │ │ └── user_from_email.cr │ │ ├── request_password_reset.cr │ │ ├── reset_password.cr │ │ ├── sign_in_user.cr │ │ └── sign_up_user.cr │ │ └── queries │ │ └── user_query.cr ├── browser_authentication_src_template │ └── expected │ │ ├── spec │ │ ├── flows │ │ │ ├── authentication_spec.cr │ │ │ └── reset_password_spec.cr │ │ └── support │ │ │ ├── .keep │ │ │ └── flows │ │ │ ├── authentication_flow.cr │ │ │ └── reset_password_flow.cr │ │ └── src │ │ ├── actions │ │ ├── me │ │ │ └── show.cr │ │ ├── mixins │ │ │ ├── .keep │ │ │ └── auth │ │ │ │ ├── allow_guests.cr │ │ │ │ ├── password_resets │ │ │ │ ├── base.cr │ │ │ │ ├── find_user.cr │ │ │ │ ├── require_token.cr │ │ │ │ └── token_from_session.cr │ │ │ │ ├── redirect_signed_in_users.cr │ │ │ │ ├── require_sign_in.cr │ │ │ │ └── test_backdoor.cr │ │ ├── password_reset_requests │ │ │ ├── create.cr │ │ │ └── new.cr │ │ ├── password_resets │ │ │ ├── create.cr │ │ │ ├── edit.cr │ │ │ └── new.cr │ │ ├── sign_ins │ │ │ ├── create.cr │ │ │ ├── delete.cr │ │ │ └── new.cr │ │ └── sign_ups │ │ │ ├── create.cr │ │ │ └── new.cr │ │ ├── emails │ │ ├── password_reset_request_email.cr │ │ └── templates │ │ │ └── password_reset_request_email │ │ │ ├── html.ecr │ │ │ └── text.ecr │ │ └── pages │ │ ├── auth_layout.cr │ │ ├── main_layout.cr │ │ ├── me │ │ └── show_page.cr │ │ ├── password_reset_requests │ │ └── new_page.cr │ │ ├── password_resets │ │ └── new_page.cr │ │ ├── sign_ins │ │ └── new_page.cr │ │ └── sign_ups │ │ └── new_page.cr ├── browser_src_template │ └── expected │ │ ├── bs-config.js │ │ ├── config │ │ └── html_page.cr │ │ ├── db │ │ └── migrations │ │ │ └── .keep │ │ ├── package.json │ │ ├── public │ │ ├── assets │ │ │ └── images │ │ │ │ └── .keep │ │ ├── favicon.ico │ │ ├── mix-manifest.json │ │ └── robots.txt │ │ ├── spec │ │ ├── flows │ │ │ └── .keep │ │ ├── setup │ │ │ ├── .keep │ │ │ └── configure_lucky_flow.cr │ │ └── support │ │ │ ├── .keep │ │ │ ├── factories │ │ │ └── .keep │ │ │ └── flows │ │ │ └── base_flow.cr │ │ ├── src │ │ ├── actions │ │ │ └── browser_action.cr │ │ ├── components │ │ │ ├── .keep │ │ │ ├── base_component.cr │ │ │ └── shared │ │ │ │ ├── field.cr │ │ │ │ ├── field_errors.cr │ │ │ │ ├── flash_messages.cr │ │ │ │ └── layout_head.cr │ │ ├── css │ │ │ └── app.scss │ │ ├── emails │ │ │ └── .keep │ │ ├── js │ │ │ └── app.js │ │ ├── models │ │ │ └── mixins │ │ │ │ └── .keep │ │ ├── operations │ │ │ ├── .keep │ │ │ └── mixins │ │ │ │ └── .keep │ │ └── pages │ │ │ ├── errors │ │ │ └── show_page.cr │ │ │ └── main_layout.cr │ │ └── webpack.mix.js ├── browser_src_template__generate_auth │ └── expected │ │ ├── bs-config.js │ │ ├── config │ │ └── html_page.cr │ │ ├── db │ │ └── migrations │ │ │ └── .keep │ │ ├── package.json │ │ ├── public │ │ ├── assets │ │ │ └── images │ │ │ │ └── .keep │ │ ├── favicon.ico │ │ ├── mix-manifest.json │ │ └── robots.txt │ │ ├── spec │ │ ├── flows │ │ │ └── .keep │ │ ├── setup │ │ │ ├── .keep │ │ │ └── configure_lucky_flow.cr │ │ └── support │ │ │ ├── .keep │ │ │ ├── factories │ │ │ └── .keep │ │ │ └── flows │ │ │ └── base_flow.cr │ │ ├── src │ │ ├── actions │ │ │ └── browser_action.cr │ │ ├── components │ │ │ ├── .keep │ │ │ ├── base_component.cr │ │ │ └── shared │ │ │ │ ├── field.cr │ │ │ │ ├── field_errors.cr │ │ │ │ ├── flash_messages.cr │ │ │ │ └── layout_head.cr │ │ ├── css │ │ │ └── app.scss │ │ ├── emails │ │ │ └── .keep │ │ ├── js │ │ │ └── app.js │ │ ├── models │ │ │ └── mixins │ │ │ │ └── .keep │ │ ├── operations │ │ │ ├── .keep │ │ │ └── mixins │ │ │ │ └── .keep │ │ └── pages │ │ │ ├── errors │ │ │ └── show_page.cr │ │ │ └── main_layout.cr │ │ └── webpack.mix.js ├── cat.gif ├── hello_crystal.cr ├── hello_world.cr ├── shard_file_template │ └── expected │ │ └── shard.yml ├── shard_file_template__browser │ └── expected │ │ └── shard.yml ├── shard_file_template__generate_auth │ └── expected │ │ └── shard.yml ├── shard_file_template__with_sec_tester │ └── expected │ │ └── shard.yml ├── src_template │ └── expected │ │ ├── .crystal-version │ │ ├── .env │ │ ├── .github │ │ └── workflows │ │ │ └── ci.yml │ │ ├── Procfile │ │ ├── Procfile.dev │ │ ├── README.md │ │ ├── config │ │ ├── application.cr │ │ ├── colors.cr │ │ ├── cookies.cr │ │ ├── database.cr │ │ ├── email.cr │ │ ├── env.cr │ │ ├── error_handler.cr │ │ ├── log.cr │ │ ├── route_helper.cr │ │ ├── server.cr │ │ └── watch.yml │ │ ├── db │ │ └── migrations │ │ │ └── .keep │ │ ├── docker-compose.yml │ │ ├── docker │ │ ├── dev_entrypoint.sh │ │ ├── development.dockerfile │ │ └── wait-for-it.sh │ │ ├── script │ │ ├── helpers │ │ │ └── function_helpers.cr │ │ ├── setup.cr │ │ └── system_check.cr │ │ ├── spec │ │ ├── setup │ │ │ ├── clean_database.cr │ │ │ ├── reset_emails.cr │ │ │ ├── setup_database.cr │ │ │ └── start_app_server.cr │ │ ├── spec_helper.cr │ │ └── support │ │ │ ├── .keep │ │ │ ├── api_client.cr │ │ │ └── factories │ │ │ └── .keep │ │ ├── src │ │ ├── actions │ │ │ ├── api_action.cr │ │ │ ├── errors │ │ │ │ └── show.cr │ │ │ ├── home │ │ │ │ └── index.cr │ │ │ └── mixins │ │ │ │ └── .keep │ │ ├── app.cr │ │ ├── app_database.cr │ │ ├── app_server.cr │ │ ├── emails │ │ │ └── base_email.cr │ │ ├── models │ │ │ ├── base_model.cr │ │ │ └── mixins │ │ │ │ └── .keep │ │ ├── operations │ │ │ ├── .keep │ │ │ └── mixins │ │ │ │ └── .keep │ │ ├── queries │ │ │ ├── .keep │ │ │ └── mixins │ │ │ │ └── .keep │ │ ├── serializers │ │ │ ├── .keep │ │ │ ├── base_serializer.cr │ │ │ └── error_serializer.cr │ │ ├── shards.cr │ │ ├── start_server.cr │ │ └── test_project.cr │ │ ├── tasks.cr │ │ └── tasks │ │ ├── .keep │ │ └── db │ │ └── seed │ │ ├── required_data.cr │ │ └── sample_data.cr ├── src_template__api_only │ └── expected │ │ ├── .crystal-version │ │ ├── .env │ │ ├── .github │ │ └── workflows │ │ │ └── ci.yml │ │ ├── Procfile │ │ ├── Procfile.dev │ │ ├── README.md │ │ ├── config │ │ ├── application.cr │ │ ├── colors.cr │ │ ├── cookies.cr │ │ ├── database.cr │ │ ├── email.cr │ │ ├── env.cr │ │ ├── error_handler.cr │ │ ├── log.cr │ │ ├── route_helper.cr │ │ ├── server.cr │ │ └── watch.yml │ │ ├── db │ │ └── migrations │ │ │ └── .keep │ │ ├── docker-compose.yml │ │ ├── docker │ │ ├── dev_entrypoint.sh │ │ ├── development.dockerfile │ │ └── wait-for-it.sh │ │ ├── script │ │ ├── helpers │ │ │ └── function_helpers.cr │ │ ├── setup.cr │ │ └── system_check.cr │ │ ├── spec │ │ ├── setup │ │ │ ├── clean_database.cr │ │ │ ├── reset_emails.cr │ │ │ ├── setup_database.cr │ │ │ └── start_app_server.cr │ │ ├── spec_helper.cr │ │ └── support │ │ │ ├── .keep │ │ │ ├── api_client.cr │ │ │ └── factories │ │ │ └── .keep │ │ ├── src │ │ ├── actions │ │ │ ├── api_action.cr │ │ │ ├── errors │ │ │ │ └── show.cr │ │ │ ├── home │ │ │ │ └── index.cr │ │ │ └── mixins │ │ │ │ └── .keep │ │ ├── app.cr │ │ ├── app_database.cr │ │ ├── app_server.cr │ │ ├── emails │ │ │ └── base_email.cr │ │ ├── models │ │ │ ├── base_model.cr │ │ │ └── mixins │ │ │ │ └── .keep │ │ ├── operations │ │ │ ├── .keep │ │ │ └── mixins │ │ │ │ └── .keep │ │ ├── queries │ │ │ ├── .keep │ │ │ └── mixins │ │ │ │ └── .keep │ │ ├── serializers │ │ │ ├── .keep │ │ │ ├── base_serializer.cr │ │ │ └── error_serializer.cr │ │ ├── shards.cr │ │ ├── start_server.cr │ │ └── test_project.cr │ │ ├── tasks.cr │ │ └── tasks │ │ ├── .keep │ │ └── db │ │ └── seed │ │ ├── required_data.cr │ │ └── sample_data.cr ├── src_template__generate_auth │ └── expected │ │ ├── .crystal-version │ │ ├── .env │ │ ├── .github │ │ └── workflows │ │ │ └── ci.yml │ │ ├── Procfile │ │ ├── Procfile.dev │ │ ├── README.md │ │ ├── config │ │ ├── application.cr │ │ ├── colors.cr │ │ ├── cookies.cr │ │ ├── database.cr │ │ ├── email.cr │ │ ├── env.cr │ │ ├── error_handler.cr │ │ ├── log.cr │ │ ├── route_helper.cr │ │ ├── server.cr │ │ └── watch.yml │ │ ├── db │ │ └── migrations │ │ │ └── .keep │ │ ├── docker-compose.yml │ │ ├── docker │ │ ├── dev_entrypoint.sh │ │ ├── development.dockerfile │ │ └── wait-for-it.sh │ │ ├── script │ │ ├── helpers │ │ │ └── function_helpers.cr │ │ ├── setup.cr │ │ └── system_check.cr │ │ ├── spec │ │ ├── setup │ │ │ ├── clean_database.cr │ │ │ ├── reset_emails.cr │ │ │ ├── setup_database.cr │ │ │ └── start_app_server.cr │ │ ├── spec_helper.cr │ │ └── support │ │ │ ├── .keep │ │ │ ├── api_client.cr │ │ │ └── factories │ │ │ └── .keep │ │ ├── src │ │ ├── actions │ │ │ ├── api_action.cr │ │ │ ├── errors │ │ │ │ └── show.cr │ │ │ ├── home │ │ │ │ └── index.cr │ │ │ └── mixins │ │ │ │ └── .keep │ │ ├── app.cr │ │ ├── app_database.cr │ │ ├── app_server.cr │ │ ├── emails │ │ │ └── base_email.cr │ │ ├── models │ │ │ ├── base_model.cr │ │ │ └── mixins │ │ │ │ └── .keep │ │ ├── operations │ │ │ ├── .keep │ │ │ └── mixins │ │ │ │ └── .keep │ │ ├── queries │ │ │ ├── .keep │ │ │ └── mixins │ │ │ │ └── .keep │ │ ├── serializers │ │ │ ├── .keep │ │ │ ├── base_serializer.cr │ │ │ └── error_serializer.cr │ │ ├── shards.cr │ │ ├── start_server.cr │ │ └── test_project.cr │ │ ├── tasks.cr │ │ └── tasks │ │ ├── .keep │ │ └── db │ │ └── seed │ │ ├── required_data.cr │ │ └── sample_data.cr ├── src_template__sec_tester │ └── expected │ │ ├── .crystal-version │ │ ├── .env │ │ ├── .github │ │ └── workflows │ │ │ └── ci.yml │ │ ├── Procfile │ │ ├── Procfile.dev │ │ ├── README.md │ │ ├── config │ │ ├── application.cr │ │ ├── colors.cr │ │ ├── cookies.cr │ │ ├── database.cr │ │ ├── email.cr │ │ ├── env.cr │ │ ├── error_handler.cr │ │ ├── log.cr │ │ ├── route_helper.cr │ │ ├── server.cr │ │ └── watch.yml │ │ ├── db │ │ └── migrations │ │ │ └── .keep │ │ ├── docker-compose.yml │ │ ├── docker │ │ ├── dev_entrypoint.sh │ │ ├── development.dockerfile │ │ └── wait-for-it.sh │ │ ├── script │ │ ├── helpers │ │ │ └── function_helpers.cr │ │ ├── setup.cr │ │ └── system_check.cr │ │ ├── spec │ │ ├── setup │ │ │ ├── clean_database.cr │ │ │ ├── reset_emails.cr │ │ │ ├── setup_database.cr │ │ │ └── start_app_server.cr │ │ ├── spec_helper.cr │ │ └── support │ │ │ ├── .keep │ │ │ ├── api_client.cr │ │ │ └── factories │ │ │ └── .keep │ │ ├── src │ │ ├── actions │ │ │ ├── api_action.cr │ │ │ ├── errors │ │ │ │ └── show.cr │ │ │ ├── home │ │ │ │ └── index.cr │ │ │ └── mixins │ │ │ │ └── .keep │ │ ├── app.cr │ │ ├── app_database.cr │ │ ├── app_server.cr │ │ ├── emails │ │ │ └── base_email.cr │ │ ├── models │ │ │ ├── base_model.cr │ │ │ └── mixins │ │ │ │ └── .keep │ │ ├── operations │ │ │ ├── .keep │ │ │ └── mixins │ │ │ │ └── .keep │ │ ├── queries │ │ │ ├── .keep │ │ │ └── mixins │ │ │ │ └── .keep │ │ ├── serializers │ │ │ ├── .keep │ │ │ ├── base_serializer.cr │ │ │ └── error_serializer.cr │ │ ├── shards.cr │ │ ├── start_server.cr │ │ └── test_project.cr │ │ ├── tasks.cr │ │ └── tasks │ │ ├── .keep │ │ └── db │ │ └── seed │ │ ├── required_data.cr │ │ └── sample_data.cr └── tasks.cr ├── shard.edge.yml ├── shard.override.yml ├── shard.yml ├── spec ├── integration │ └── lucky_cli_spec.cr ├── spec_helper.cr ├── support │ └── tempfile.cr └── unit │ ├── api_authentication_template_spec.cr │ ├── app_with_sec_tester_template_spec.cr │ ├── base_authentication_src_template_spec.cr │ ├── browser_authentication_src_template_spec.cr │ ├── browser_src_template_spec.cr │ ├── project_name_spec.cr │ ├── shard_file_generator_spec.cr │ └── src_template_spec.cr └── src ├── api_authentication_app_skeleton ├── spec │ └── requests │ │ └── api │ │ ├── me │ │ └── show_spec.cr.ecr │ │ ├── sign_ins │ │ └── create_spec.cr.ecr │ │ └── sign_ups │ │ └── create_spec.cr.ecr └── src │ ├── actions │ ├── api │ │ ├── me │ │ │ └── show.cr.ecr │ │ ├── sign_ins │ │ │ └── create.cr.ecr │ │ └── sign_ups │ │ │ └── create.cr.ecr │ └── mixins │ │ └── api │ │ └── auth │ │ ├── helpers.cr.ecr │ │ ├── require_auth_token.cr.ecr │ │ └── skip_require_auth_token.cr.ecr │ ├── models │ └── user_token.cr.ecr │ └── serializers │ └── user_serializer.cr.ecr ├── app_with_sec_tester └── spec │ ├── flows │ └── security_spec.cr.ecr │ └── setup │ └── sec_tester.cr.ecr ├── base_authentication_app_skeleton ├── config │ └── authentic.cr.ecr ├── db │ └── migrations │ │ ├── .keep │ │ └── 00000000000001_create_users.cr.ecr ├── spec │ └── support │ │ ├── .keep │ │ └── factories │ │ └── user_factory.cr.ecr └── src │ ├── models │ └── user.cr.ecr │ ├── operations │ ├── .keep │ ├── mixins │ │ ├── .keep │ │ ├── password_validations.cr.ecr │ │ └── user_from_email.cr.ecr │ ├── request_password_reset.cr.ecr │ ├── reset_password.cr.ecr │ ├── sign_in_user.cr.ecr │ └── sign_up_user.cr.ecr │ └── queries │ └── user_query.cr.ecr ├── browser_app_skeleton ├── bs-config.js.ecr ├── config │ └── html_page.cr.ecr ├── db │ └── migrations │ │ └── .keep ├── package.json.ecr ├── public │ ├── assets │ │ └── images │ │ │ └── .keep │ ├── favicon.ico.ecr │ ├── mix-manifest.json.ecr │ └── robots.txt.ecr ├── spec │ ├── flows │ │ └── .keep │ ├── setup │ │ ├── .keep │ │ └── configure_lucky_flow.cr.ecr │ └── support │ │ ├── .keep │ │ ├── factories │ │ └── .keep │ │ └── flows │ │ └── base_flow.cr.ecr ├── src │ ├── actions │ │ └── browser_action.cr.ecr │ ├── components │ │ ├── .keep │ │ ├── base_component.cr.ecr │ │ └── shared │ │ │ ├── field.cr.ecr │ │ │ ├── field_errors.cr.ecr │ │ │ ├── flash_messages.cr.ecr │ │ │ └── layout_head.cr.ecr │ ├── css │ │ ├── app.scss.ecr │ │ ├── components │ │ │ └── .keep │ │ ├── mixins │ │ │ └── .keep │ │ └── variables │ │ │ └── .keep │ ├── emails │ │ └── .keep │ ├── js │ │ └── app.js.ecr │ ├── models │ │ └── mixins │ │ │ └── .keep │ ├── operations │ │ ├── .keep │ │ └── mixins │ │ │ └── .keep │ └── pages │ │ ├── errors │ │ └── show_page.cr.ecr │ │ └── main_layout.cr.ecr └── webpack.mix.js.ecr ├── browser_authentication_app_skeleton ├── spec │ ├── flows │ │ ├── authentication_spec.cr.ecr │ │ └── reset_password_spec.cr.ecr │ └── support │ │ ├── .keep │ │ └── flows │ │ ├── authentication_flow.cr.ecr │ │ └── reset_password_flow.cr.ecr └── src │ ├── actions │ ├── me │ │ └── show.cr.ecr │ ├── mixins │ │ ├── .keep │ │ └── auth │ │ │ ├── allow_guests.cr.ecr │ │ │ ├── password_resets │ │ │ ├── base.cr.ecr │ │ │ ├── find_user.cr.ecr │ │ │ ├── require_token.cr.ecr │ │ │ └── token_from_session.cr.ecr │ │ │ ├── redirect_signed_in_users.cr.ecr │ │ │ ├── require_sign_in.cr.ecr │ │ │ └── test_backdoor.cr.ecr │ ├── password_reset_requests │ │ ├── create.cr.ecr │ │ └── new.cr.ecr │ ├── password_resets │ │ ├── create.cr.ecr │ │ ├── edit.cr.ecr │ │ └── new.cr.ecr │ ├── sign_ins │ │ ├── create.cr.ecr │ │ ├── delete.cr.ecr │ │ └── new.cr.ecr │ └── sign_ups │ │ ├── create.cr.ecr │ │ └── new.cr.ecr │ ├── emails │ ├── password_reset_request_email.cr.ecr │ └── templates │ │ └── password_reset_request_email │ │ ├── html.ecr.ecr │ │ └── text.ecr.ecr │ └── pages │ ├── auth_layout.cr.ecr │ ├── main_layout.cr.ecr │ ├── me │ └── show_page.cr.ecr │ ├── password_reset_requests │ └── new_page.cr.ecr │ ├── password_resets │ └── new_page.cr.ecr │ ├── sign_ins │ └── new_page.cr.ecr │ └── sign_ups │ └── new_page.cr.ecr ├── build_and_run_task.cr ├── dev.cr ├── generators └── web.cr ├── init.cr ├── init_custom.cr ├── lucky.cr ├── lucky_cli.cr ├── lucky_cli ├── api_authentication_template.cr ├── app_with_sec_tester_template.cr ├── base_authentication_src_template.cr ├── browser_authentication_src_template.cr ├── browser_src_template.cr ├── generator_helpers.cr ├── project_name.cr ├── shard_file_generator.cr ├── spinner.cr ├── src_template.cr ├── version.cr └── wizard │ ├── labeled_yes_no_question.cr │ ├── project_name_question.cr │ ├── web.cr │ └── yes_no_question.cr └── web_app_skeleton ├── .crystal-version.ecr ├── .env.ecr ├── .github └── workflows │ └── ci.yml.ecr ├── Procfile.dev.ecr ├── Procfile.ecr ├── README.md.ecr ├── config ├── application.cr.ecr ├── colors.cr.ecr ├── cookies.cr.ecr ├── database.cr.ecr ├── email.cr.ecr ├── env.cr.ecr ├── error_handler.cr.ecr ├── log.cr.ecr ├── route_helper.cr.ecr ├── server.cr.ecr └── watch.yml.ecr ├── db └── migrations │ └── .keep ├── docker-compose.yml.ecr ├── docker ├── dev_entrypoint.sh.ecr ├── development.dockerfile.ecr └── wait-for-it.sh.ecr ├── script ├── helpers │ └── function_helpers.cr.ecr ├── setup.cr.ecr └── system_check.cr.ecr ├── spec ├── setup │ ├── clean_database.cr.ecr │ ├── reset_emails.cr.ecr │ ├── setup_database.cr.ecr │ └── start_app_server.cr.ecr ├── spec_helper.cr.ecr └── support │ ├── .keep │ ├── api_client.cr.ecr │ └── factories │ └── .keep ├── src ├── actions │ ├── api_action.cr.ecr │ ├── errors │ │ └── show.cr.ecr │ ├── home │ │ └── index.cr.ecr │ └── mixins │ │ └── .keep ├── app.cr.ecr ├── app_database.cr.ecr ├── app_server.cr.ecr ├── emails │ └── base_email.cr.ecr ├── models │ ├── base_model.cr.ecr │ └── mixins │ │ └── .keep ├── operations │ ├── .keep │ └── mixins │ │ └── .keep ├── project_name.cr.ecr ├── queries │ ├── .keep │ └── mixins │ │ └── .keep ├── serializers │ ├── .keep │ ├── base_serializer.cr.ecr │ └── error_serializer.cr.ecr ├── shards.cr.ecr └── start_server.cr.ecr ├── tasks.cr.ecr └── tasks ├── .keep └── db └── seed ├── required_data.cr.ecr └── sample_data.cr.ecr /.env.sample: -------------------------------------------------------------------------------- 1 | # Change 0 -> 1 if you want to run Heroku specs 2 | RUN_HEROKU_SPECS=0 3 | HEROKU_EMAIL=foo@example.com 4 | # Get this by running -> heroku authorizations:create 5 | # Then copy the token here: 6 | HEROKU_API_KEY=fake-token 7 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | 3 | updates: 4 | - package-ecosystem: "github-actions" 5 | directory: "/" 6 | schedule: 7 | interval: monthly 8 | target-branch: main 9 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /doc/ 2 | /libs/ 3 | /lib/ 4 | /bin 5 | /.shards/ 6 | lucky 7 | *.dwarf 8 | .env 9 | /test-project/ 10 | node_modules 11 | /tmp/ 12 | !/fixtures/*/expected/**/* 13 | 14 | # Libraries don't need dependency lock 15 | # Dependencies will be locked in application that uses them 16 | /shard.lock 17 | yarn.lock 18 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3' 2 | 3 | services: 4 | postgres: 5 | image: postgres:16 6 | restart: always 7 | environment: 8 | POSTGRES_USER: postgres 9 | POSTGRES_PASSWORD: postgres 10 | TZ: "UTC" 11 | ports: 12 | - 5432:5432 13 | hostname: postgres 14 | healthcheck: 15 | test: ["CMD", "pg_isready"] 16 | interval: 10s 17 | timeout: 5s 18 | retries: 5 19 | -------------------------------------------------------------------------------- /fixtures/api_authentication_template/expected/spec/requests/api/me/show_spec.cr: -------------------------------------------------------------------------------- 1 | require "../../../spec_helper" 2 | 3 | describe Api::Me::Show do 4 | it "returns the signed in user" do 5 | user = UserFactory.create 6 | 7 | response = ApiClient.auth(user).exec(Api::Me::Show) 8 | 9 | response.should send_json(200, email: user.email) 10 | end 11 | 12 | it "fails if not authenticated" do 13 | response = ApiClient.exec(Api::Me::Show) 14 | 15 | response.status_code.should eq(401) 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /fixtures/api_authentication_template/expected/spec/requests/api/sign_ins/create_spec.cr: -------------------------------------------------------------------------------- 1 | require "../../../spec_helper" 2 | 3 | describe Api::SignIns::Create do 4 | it "returns a token" do 5 | UserToken.stub_token("fake-token") do 6 | user = UserFactory.create 7 | 8 | response = ApiClient.exec(Api::SignIns::Create, user: valid_params(user)) 9 | 10 | response.should send_json(200, token: "fake-token") 11 | end 12 | end 13 | 14 | it "returns an error if credentials are invalid" do 15 | user = UserFactory.create 16 | invalid_params = valid_params(user).merge(password: "incorrect") 17 | 18 | response = ApiClient.exec(Api::SignIns::Create, user: invalid_params) 19 | 20 | response.should send_json( 21 | 400, 22 | param: "password", 23 | details: "password is wrong" 24 | ) 25 | end 26 | end 27 | 28 | private def valid_params(user : User) 29 | { 30 | email: user.email, 31 | password: "password", 32 | } 33 | end 34 | -------------------------------------------------------------------------------- /fixtures/api_authentication_template/expected/src/actions/api/me/show.cr: -------------------------------------------------------------------------------- 1 | class Api::Me::Show < ApiAction 2 | get "/api/me" do 3 | json UserSerializer.new(current_user) 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /fixtures/api_authentication_template/expected/src/actions/api/sign_ins/create.cr: -------------------------------------------------------------------------------- 1 | class Api::SignIns::Create < ApiAction 2 | include Api::Auth::SkipRequireAuthToken 3 | 4 | post "/api/sign_ins" do 5 | SignInUser.run(params) do |operation, user| 6 | if user 7 | json({token: UserToken.generate(user)}) 8 | else 9 | raise Avram::InvalidOperationError.new(operation) 10 | end 11 | end 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /fixtures/api_authentication_template/expected/src/actions/api/sign_ups/create.cr: -------------------------------------------------------------------------------- 1 | class Api::SignUps::Create < ApiAction 2 | include Api::Auth::SkipRequireAuthToken 3 | 4 | post "/api/sign_ups" do 5 | user = SignUpUser.create!(params) 6 | json({token: UserToken.generate(user)}) 7 | end 8 | end 9 | -------------------------------------------------------------------------------- /fixtures/api_authentication_template/expected/src/actions/mixins/api/auth/helpers.cr: -------------------------------------------------------------------------------- 1 | module Api::Auth::Helpers 2 | # The 'memoize' macro makes sure only one query is issued to find the user 3 | memoize def current_user? : User? 4 | auth_token.try do |value| 5 | user_from_auth_token(value) 6 | end 7 | end 8 | 9 | private def auth_token : String? 10 | bearer_token || token_param 11 | end 12 | 13 | private def bearer_token : String? 14 | context.request.headers["Authorization"]? 15 | .try(&.gsub("Bearer", "")) 16 | .try(&.strip) 17 | end 18 | 19 | private def token_param : String? 20 | params.get?(:auth_token) 21 | end 22 | 23 | private def user_from_auth_token(token : String) : User? 24 | UserToken.decode_user_id(token).try do |user_id| 25 | UserQuery.new.id(user_id).first? 26 | end 27 | end 28 | end 29 | -------------------------------------------------------------------------------- /fixtures/api_authentication_template/expected/src/actions/mixins/api/auth/skip_require_auth_token.cr: -------------------------------------------------------------------------------- 1 | module Api::Auth::SkipRequireAuthToken 2 | macro included 3 | skip require_auth_token 4 | end 5 | 6 | # Since sign in is not required, current_user might be nil 7 | def current_user : User? 8 | current_user? 9 | end 10 | end 11 | -------------------------------------------------------------------------------- /fixtures/api_authentication_template/expected/src/serializers/user_serializer.cr: -------------------------------------------------------------------------------- 1 | class UserSerializer < BaseSerializer 2 | def initialize(@user : User) 3 | end 4 | 5 | def render 6 | {email: @user.email} 7 | end 8 | end 9 | -------------------------------------------------------------------------------- /fixtures/app_sec_tester_template/expected/spec/setup/sec_tester.cr: -------------------------------------------------------------------------------- 1 | require "lucky_sec_tester" 2 | 3 | # Signup for a `BRIGHT_TOKEN` at 4 | # [Bright Security](https://app.neuralegion.com/signup) 5 | # Read more about the SecTester on https://github.com/luckyframework/lucky_sec_tester 6 | LuckySecTester.configure do |setting| 7 | setting.bright_token = ENV["BRIGHT_TOKEN"] 8 | setting.project_id = ENV["BRIGHT_PROJECT_ID"] 9 | end 10 | -------------------------------------------------------------------------------- /fixtures/app_sec_tester_template__browser/expected/spec/setup/sec_tester.cr: -------------------------------------------------------------------------------- 1 | require "lucky_sec_tester" 2 | 3 | # Signup for a `BRIGHT_TOKEN` at 4 | # [Bright Security](https://app.neuralegion.com/signup) 5 | # Read more about the SecTester on https://github.com/luckyframework/lucky_sec_tester 6 | LuckySecTester.configure do |setting| 7 | setting.bright_token = ENV["BRIGHT_TOKEN"] 8 | setting.project_id = ENV["BRIGHT_PROJECT_ID"] 9 | end 10 | -------------------------------------------------------------------------------- /fixtures/app_sec_tester_template__generate_auth/expected/spec/setup/sec_tester.cr: -------------------------------------------------------------------------------- 1 | require "lucky_sec_tester" 2 | 3 | # Signup for a `BRIGHT_TOKEN` at 4 | # [Bright Security](https://app.neuralegion.com/signup) 5 | # Read more about the SecTester on https://github.com/luckyframework/lucky_sec_tester 6 | LuckySecTester.configure do |setting| 7 | setting.bright_token = ENV["BRIGHT_TOKEN"] 8 | setting.project_id = ENV["BRIGHT_PROJECT_ID"] 9 | end 10 | -------------------------------------------------------------------------------- /fixtures/app_sec_tester_template__no_browser_auth/expected/spec/flows/security_spec.cr: -------------------------------------------------------------------------------- 1 | {% skip_file unless flag?(:with_sec_tests) %} 2 | # Run these specs with `crystal spec -Dwith_sec_tests` 3 | 4 | require "../spec_helper" 5 | 6 | describe "SecTester" do 7 | end 8 | -------------------------------------------------------------------------------- /fixtures/app_sec_tester_template__no_browser_auth/expected/spec/setup/sec_tester.cr: -------------------------------------------------------------------------------- 1 | require "lucky_sec_tester" 2 | 3 | # Signup for a `BRIGHT_TOKEN` at 4 | # [Bright Security](https://app.neuralegion.com/signup) 5 | # Read more about the SecTester on https://github.com/luckyframework/lucky_sec_tester 6 | LuckySecTester.configure do |setting| 7 | setting.bright_token = ENV["BRIGHT_TOKEN"] 8 | setting.project_id = ENV["BRIGHT_PROJECT_ID"] 9 | end 10 | -------------------------------------------------------------------------------- /fixtures/base_authentication_src_template/expected/config/authentic.cr: -------------------------------------------------------------------------------- 1 | require "./server" 2 | 3 | Authentic.configure do |settings| 4 | settings.secret_key = Lucky::Server.settings.secret_key_base 5 | 6 | unless LuckyEnv.production? 7 | # This value can be between 4 and 31 8 | fastest_encryption_possible = 4 9 | settings.encryption_cost = fastest_encryption_possible 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /fixtures/base_authentication_src_template/expected/db/migrations/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luckyframework/lucky_cli/ccaa571765582086d437efe0da4f439a19929d64/fixtures/base_authentication_src_template/expected/db/migrations/.keep -------------------------------------------------------------------------------- /fixtures/base_authentication_src_template/expected/db/migrations/00000000000001_create_users.cr: -------------------------------------------------------------------------------- 1 | class CreateUsers::V00000000000001 < Avram::Migrator::Migration::V1 2 | def migrate 3 | enable_extension "citext" 4 | 5 | create table_for(User) do 6 | primary_key id : Int64 7 | add_timestamps 8 | add email : String, unique: true, case_sensitive: false 9 | add encrypted_password : String 10 | end 11 | end 12 | 13 | def rollback 14 | drop table_for(User) 15 | disable_extension "citext" 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /fixtures/base_authentication_src_template/expected/spec/support/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luckyframework/lucky_cli/ccaa571765582086d437efe0da4f439a19929d64/fixtures/base_authentication_src_template/expected/spec/support/.keep -------------------------------------------------------------------------------- /fixtures/base_authentication_src_template/expected/spec/support/factories/user_factory.cr: -------------------------------------------------------------------------------- 1 | class UserFactory < Avram::Factory 2 | def initialize 3 | email "#{sequence("test-email")}@example.com" 4 | encrypted_password Authentic.generate_encrypted_password("password") 5 | end 6 | end 7 | -------------------------------------------------------------------------------- /fixtures/base_authentication_src_template/expected/src/models/user.cr: -------------------------------------------------------------------------------- 1 | class User < BaseModel 2 | include Carbon::Emailable 3 | include Authentic::PasswordAuthenticatable 4 | 5 | table do 6 | column email : String 7 | column encrypted_password : String 8 | end 9 | 10 | def emailable : Carbon::Address 11 | Carbon::Address.new(email) 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /fixtures/base_authentication_src_template/expected/src/operations/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luckyframework/lucky_cli/ccaa571765582086d437efe0da4f439a19929d64/fixtures/base_authentication_src_template/expected/src/operations/.keep -------------------------------------------------------------------------------- /fixtures/base_authentication_src_template/expected/src/operations/mixins/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luckyframework/lucky_cli/ccaa571765582086d437efe0da4f439a19929d64/fixtures/base_authentication_src_template/expected/src/operations/mixins/.keep -------------------------------------------------------------------------------- /fixtures/base_authentication_src_template/expected/src/operations/mixins/password_validations.cr: -------------------------------------------------------------------------------- 1 | module PasswordValidations 2 | macro included 3 | before_save run_password_validations 4 | end 5 | 6 | private def run_password_validations 7 | validate_required password, password_confirmation 8 | validate_confirmation_of password, with: password_confirmation 9 | # 72 is a limitation of BCrypt 10 | validate_size_of password, min: 6, max: 72 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /fixtures/base_authentication_src_template/expected/src/operations/mixins/user_from_email.cr: -------------------------------------------------------------------------------- 1 | module UserFromEmail 2 | private def user_from_email : User? 3 | email.value.try do |value| 4 | UserQuery.new.email(value).first? 5 | end 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /fixtures/base_authentication_src_template/expected/src/operations/request_password_reset.cr: -------------------------------------------------------------------------------- 1 | class RequestPasswordReset < Avram::Operation 2 | # You can modify this in src/operations/mixins/user_from_email.cr 3 | include UserFromEmail 4 | 5 | attribute email : String 6 | 7 | # Run validations and yield the operation and the user if valid 8 | def run 9 | user = user_from_email 10 | validate(user) 11 | 12 | if valid? 13 | user 14 | else 15 | nil 16 | end 17 | end 18 | 19 | def validate(user : User?) 20 | validate_required email 21 | if user.nil? 22 | email.add_error "is not in our system" 23 | end 24 | end 25 | end 26 | -------------------------------------------------------------------------------- /fixtures/base_authentication_src_template/expected/src/operations/reset_password.cr: -------------------------------------------------------------------------------- 1 | class ResetPassword < User::SaveOperation 2 | # Change password validations in src/operations/mixins/password_validations.cr 3 | include PasswordValidations 4 | 5 | attribute password : String 6 | attribute password_confirmation : String 7 | 8 | before_save do 9 | Authentic.copy_and_encrypt password, to: encrypted_password 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /fixtures/base_authentication_src_template/expected/src/operations/sign_up_user.cr: -------------------------------------------------------------------------------- 1 | class SignUpUser < User::SaveOperation 2 | param_key :user 3 | # Change password validations in src/operations/mixins/password_validations.cr 4 | include PasswordValidations 5 | 6 | permit_columns email 7 | attribute password : String 8 | attribute password_confirmation : String 9 | 10 | before_save do 11 | validate_uniqueness_of email 12 | Authentic.copy_and_encrypt(password, to: encrypted_password) if password.valid? 13 | end 14 | end 15 | -------------------------------------------------------------------------------- /fixtures/base_authentication_src_template/expected/src/queries/user_query.cr: -------------------------------------------------------------------------------- 1 | class UserQuery < User::BaseQuery 2 | end 3 | -------------------------------------------------------------------------------- /fixtures/browser_authentication_src_template/expected/spec/flows/reset_password_spec.cr: -------------------------------------------------------------------------------- 1 | require "../spec_helper" 2 | 3 | describe "Reset password flow", tags: "flow" do 4 | it "works" do 5 | user = UserFactory.create 6 | flow = ResetPasswordFlow.new(user) 7 | 8 | flow.request_password_reset 9 | flow.should_have_sent_reset_email 10 | flow.reset_password "new-password" 11 | flow.should_be_signed_in 12 | flow.sign_out 13 | flow.sign_in "wrong-password" 14 | flow.should_have_password_error 15 | flow.sign_in "new-password" 16 | flow.should_be_signed_in 17 | end 18 | end 19 | -------------------------------------------------------------------------------- /fixtures/browser_authentication_src_template/expected/spec/support/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luckyframework/lucky_cli/ccaa571765582086d437efe0da4f439a19929d64/fixtures/browser_authentication_src_template/expected/spec/support/.keep -------------------------------------------------------------------------------- /fixtures/browser_authentication_src_template/expected/src/actions/me/show.cr: -------------------------------------------------------------------------------- 1 | class Me::Show < BrowserAction 2 | get "/me" do 3 | html ShowPage 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /fixtures/browser_authentication_src_template/expected/src/actions/mixins/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luckyframework/lucky_cli/ccaa571765582086d437efe0da4f439a19929d64/fixtures/browser_authentication_src_template/expected/src/actions/mixins/.keep -------------------------------------------------------------------------------- /fixtures/browser_authentication_src_template/expected/src/actions/mixins/auth/allow_guests.cr: -------------------------------------------------------------------------------- 1 | module Auth::AllowGuests 2 | macro included 3 | skip require_sign_in 4 | end 5 | 6 | # Since sign in is not required, current_user might be nil 7 | def current_user : User? 8 | current_user? 9 | end 10 | end 11 | -------------------------------------------------------------------------------- /fixtures/browser_authentication_src_template/expected/src/actions/mixins/auth/password_resets/base.cr: -------------------------------------------------------------------------------- 1 | module Auth::PasswordResets::Base 2 | macro included 3 | include Auth::RedirectSignedInUsers 4 | include Auth::PasswordResets::FindUser 5 | include Auth::PasswordResets::RequireToken 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /fixtures/browser_authentication_src_template/expected/src/actions/mixins/auth/password_resets/find_user.cr: -------------------------------------------------------------------------------- 1 | module Auth::PasswordResets::FindUser 2 | private def user : User 3 | UserQuery.find(user_id) 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /fixtures/browser_authentication_src_template/expected/src/actions/mixins/auth/password_resets/require_token.cr: -------------------------------------------------------------------------------- 1 | module Auth::PasswordResets::RequireToken 2 | macro included 3 | before require_valid_password_reset_token 4 | end 5 | 6 | abstract def token : String 7 | abstract def user : User 8 | 9 | private def require_valid_password_reset_token 10 | if Authentic.valid_password_reset_token?(user, token) 11 | continue 12 | else 13 | flash.failure = "The password reset link is incorrect or expired. Please try again." 14 | redirect to: PasswordResetRequests::New 15 | end 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /fixtures/browser_authentication_src_template/expected/src/actions/mixins/auth/password_resets/token_from_session.cr: -------------------------------------------------------------------------------- 1 | module Auth::PasswordResets::TokenFromSession 2 | private def token : String 3 | session.get?(:password_reset_token) || raise "Password reset token not found in session" 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /fixtures/browser_authentication_src_template/expected/src/actions/mixins/auth/redirect_signed_in_users.cr: -------------------------------------------------------------------------------- 1 | module Auth::RedirectSignedInUsers 2 | macro included 3 | include Auth::AllowGuests 4 | before redirect_signed_in_users 5 | end 6 | 7 | private def redirect_signed_in_users 8 | if current_user? 9 | flash.success = "You are already signed in" 10 | redirect to: Home::Index 11 | else 12 | continue 13 | end 14 | end 15 | 16 | # current_user returns nil because signed in users are redirected. 17 | def current_user 18 | end 19 | end 20 | -------------------------------------------------------------------------------- /fixtures/browser_authentication_src_template/expected/src/actions/mixins/auth/require_sign_in.cr: -------------------------------------------------------------------------------- 1 | module Auth::RequireSignIn 2 | macro included 3 | before require_sign_in 4 | end 5 | 6 | private def require_sign_in 7 | if current_user? 8 | continue 9 | else 10 | Authentic.remember_requested_path(self) 11 | flash.info = "Please sign in first" 12 | redirect to: SignIns::New 13 | end 14 | end 15 | 16 | # Tells the compiler that the current_user is not nil since we have checked 17 | # that the user is signed in 18 | private def current_user : User 19 | current_user?.as(User) 20 | end 21 | end 22 | -------------------------------------------------------------------------------- /fixtures/browser_authentication_src_template/expected/src/actions/mixins/auth/test_backdoor.cr: -------------------------------------------------------------------------------- 1 | module Auth::TestBackdoor 2 | macro included 3 | before test_backdoor 4 | end 5 | 6 | private def test_backdoor 7 | if LuckyEnv.test? && (user_id = params.get?(:backdoor_user_id)) 8 | user = UserQuery.find(user_id) 9 | sign_in user 10 | end 11 | continue 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /fixtures/browser_authentication_src_template/expected/src/actions/password_reset_requests/create.cr: -------------------------------------------------------------------------------- 1 | class PasswordResetRequests::Create < BrowserAction 2 | include Auth::RedirectSignedInUsers 3 | 4 | post "/password_reset_requests" do 5 | RequestPasswordReset.run(params) do |operation, user| 6 | if user 7 | PasswordResetRequestEmail.new(user).deliver 8 | flash.success = "You should receive an email on how to reset your password shortly" 9 | redirect SignIns::New 10 | else 11 | html NewPage, operation: operation 12 | end 13 | end 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /fixtures/browser_authentication_src_template/expected/src/actions/password_reset_requests/new.cr: -------------------------------------------------------------------------------- 1 | class PasswordResetRequests::New < BrowserAction 2 | include Auth::RedirectSignedInUsers 3 | 4 | get "/password_reset_requests/new" do 5 | html NewPage, operation: RequestPasswordReset.new 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /fixtures/browser_authentication_src_template/expected/src/actions/password_resets/create.cr: -------------------------------------------------------------------------------- 1 | class PasswordResets::Create < BrowserAction 2 | include Auth::PasswordResets::Base 3 | include Auth::PasswordResets::TokenFromSession 4 | 5 | post "/password_resets/:user_id" do 6 | ResetPassword.update(user, params) do |operation, user| 7 | if operation.saved? 8 | session.delete(:password_reset_token) 9 | sign_in user 10 | flash.success = "Your password has been reset" 11 | redirect to: Home::Index 12 | else 13 | html NewPage, operation: operation, user_id: user_id.to_i64 14 | end 15 | end 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /fixtures/browser_authentication_src_template/expected/src/actions/password_resets/edit.cr: -------------------------------------------------------------------------------- 1 | class PasswordResets::Edit < BrowserAction 2 | include Auth::PasswordResets::Base 3 | include Auth::PasswordResets::TokenFromSession 4 | 5 | get "/password_resets/:user_id/edit" do 6 | html NewPage, operation: ResetPassword.new, user_id: user_id.to_i64 7 | end 8 | end 9 | -------------------------------------------------------------------------------- /fixtures/browser_authentication_src_template/expected/src/actions/password_resets/new.cr: -------------------------------------------------------------------------------- 1 | class PasswordResets::New < BrowserAction 2 | include Auth::PasswordResets::Base 3 | 4 | param token : String 5 | 6 | get "/password_resets/:user_id" do 7 | redirect_to_edit_form_without_token_param 8 | end 9 | 10 | # This is to prevent password reset tokens from being scraped in the HTTP Referer header 11 | # See more info here: https://github.com/thoughtbot/clearance/pull/707 12 | private def redirect_to_edit_form_without_token_param 13 | make_token_available_to_future_actions 14 | redirect to: PasswordResets::Edit.with(user_id) 15 | end 16 | 17 | private def make_token_available_to_future_actions 18 | session.set(:password_reset_token, token) 19 | end 20 | end 21 | -------------------------------------------------------------------------------- /fixtures/browser_authentication_src_template/expected/src/actions/sign_ins/create.cr: -------------------------------------------------------------------------------- 1 | class SignIns::Create < BrowserAction 2 | include Auth::RedirectSignedInUsers 3 | 4 | post "/sign_in" do 5 | SignInUser.run(params) do |operation, authenticated_user| 6 | if authenticated_user 7 | sign_in(authenticated_user) 8 | flash.success = "You're now signed in" 9 | Authentic.redirect_to_originally_requested_path(self, fallback: Home::Index) 10 | else 11 | flash.failure = "Sign in failed" 12 | html NewPage, operation: operation 13 | end 14 | end 15 | end 16 | end 17 | -------------------------------------------------------------------------------- /fixtures/browser_authentication_src_template/expected/src/actions/sign_ins/delete.cr: -------------------------------------------------------------------------------- 1 | class SignIns::Delete < BrowserAction 2 | delete "/sign_out" do 3 | sign_out 4 | flash.info = "You have been signed out" 5 | redirect to: SignIns::New 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /fixtures/browser_authentication_src_template/expected/src/actions/sign_ins/new.cr: -------------------------------------------------------------------------------- 1 | class SignIns::New < BrowserAction 2 | include Auth::RedirectSignedInUsers 3 | 4 | get "/sign_in" do 5 | html NewPage, operation: SignInUser.new 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /fixtures/browser_authentication_src_template/expected/src/actions/sign_ups/create.cr: -------------------------------------------------------------------------------- 1 | class SignUps::Create < BrowserAction 2 | include Auth::RedirectSignedInUsers 3 | 4 | post "/sign_up" do 5 | SignUpUser.create(params) do |operation, user| 6 | if user 7 | flash.info = "Thanks for signing up" 8 | sign_in(user) 9 | redirect to: Home::Index 10 | else 11 | flash.info = "Couldn't sign you up" 12 | html NewPage, operation: operation 13 | end 14 | end 15 | end 16 | end 17 | -------------------------------------------------------------------------------- /fixtures/browser_authentication_src_template/expected/src/actions/sign_ups/new.cr: -------------------------------------------------------------------------------- 1 | class SignUps::New < BrowserAction 2 | include Auth::RedirectSignedInUsers 3 | 4 | get "/sign_up" do 5 | html NewPage, operation: SignUpUser.new 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /fixtures/browser_authentication_src_template/expected/src/emails/password_reset_request_email.cr: -------------------------------------------------------------------------------- 1 | class PasswordResetRequestEmail < BaseEmail 2 | Habitat.create { setting stubbed_token : String? } 3 | delegate stubbed_token, to: :settings 4 | 5 | def initialize(@user : User) 6 | @token = stubbed_token || Authentic.generate_password_reset_token(@user) 7 | end 8 | 9 | to @user 10 | from "myapp@support.com" # or set a default in src/emails/base_email.cr 11 | subject "Reset your password" 12 | templates html, text 13 | end 14 | -------------------------------------------------------------------------------- /fixtures/browser_authentication_src_template/expected/src/emails/templates/password_reset_request_email/html.ecr: -------------------------------------------------------------------------------- 1 |

Please reset your password

2 | 3 | Reset password 4 | -------------------------------------------------------------------------------- /fixtures/browser_authentication_src_template/expected/src/emails/templates/password_reset_request_email/text.ecr: -------------------------------------------------------------------------------- 1 | Please reset your password: 2 | 3 | <%= PasswordResets::New.url(@user.id, @token) %> 4 | -------------------------------------------------------------------------------- /fixtures/browser_authentication_src_template/expected/src/pages/auth_layout.cr: -------------------------------------------------------------------------------- 1 | abstract class AuthLayout 2 | include Lucky::HTMLPage 3 | 4 | abstract def content 5 | abstract def page_title 6 | 7 | # The default page title. It is passed to `Shared::LayoutHead`. 8 | # 9 | # Add a `page_title` method to pages to override it. You can also remove 10 | # This method so every page is required to have its own page title. 11 | def page_title 12 | "Welcome" 13 | end 14 | 15 | def render 16 | html_doctype 17 | 18 | html lang: "en" do 19 | mount Shared::LayoutHead, page_title: page_title 20 | 21 | body do 22 | mount Shared::FlashMessages, context.flash 23 | content 24 | end 25 | end 26 | end 27 | end 28 | -------------------------------------------------------------------------------- /fixtures/browser_authentication_src_template/expected/src/pages/me/show_page.cr: -------------------------------------------------------------------------------- 1 | class Me::ShowPage < MainLayout 2 | def content 3 | h1 "This is your profile" 4 | h3 "Email: #{@current_user.email}" 5 | helpful_tips 6 | end 7 | 8 | private def helpful_tips 9 | h3 "Next, you may want to:" 10 | ul do 11 | li { link_to_authentication_guides } 12 | li "Modify this page: src/pages/me/show_page.cr" 13 | li "Change where you go after sign in: src/actions/home/index.cr" 14 | end 15 | end 16 | 17 | private def link_to_authentication_guides 18 | a "Check out the authentication guides", 19 | href: "https://luckyframework.org/guides/authentication" 20 | end 21 | end 22 | -------------------------------------------------------------------------------- /fixtures/browser_authentication_src_template/expected/src/pages/password_reset_requests/new_page.cr: -------------------------------------------------------------------------------- 1 | class PasswordResetRequests::NewPage < AuthLayout 2 | needs operation : RequestPasswordReset 3 | 4 | def content 5 | h1 "Reset your password" 6 | render_form(@operation) 7 | end 8 | 9 | private def render_form(op) 10 | form_for PasswordResetRequests::Create do 11 | mount Shared::Field, attribute: op.email, label_text: "Email", &.email_input 12 | submit "Reset Password", flow_id: "request-password-reset-button" 13 | end 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /fixtures/browser_authentication_src_template/expected/src/pages/password_resets/new_page.cr: -------------------------------------------------------------------------------- 1 | class PasswordResets::NewPage < AuthLayout 2 | needs operation : ResetPassword 3 | needs user_id : Int64 4 | 5 | def content 6 | h1 "Reset your password" 7 | render_password_reset_form(@operation) 8 | end 9 | 10 | private def render_password_reset_form(op) 11 | form_for PasswordResets::Create.with(@user_id) do 12 | mount Shared::Field, attribute: op.password, label_text: "Password", &.password_input(autofocus: "true") 13 | mount Shared::Field, attribute: op.password_confirmation, label_text: "Confirm Password", &.password_input 14 | 15 | submit "Update Password", flow_id: "update-password-button" 16 | end 17 | end 18 | end 19 | -------------------------------------------------------------------------------- /fixtures/browser_authentication_src_template/expected/src/pages/sign_ins/new_page.cr: -------------------------------------------------------------------------------- 1 | class SignIns::NewPage < AuthLayout 2 | needs operation : SignInUser 3 | 4 | def content 5 | h1 "Sign In" 6 | render_sign_in_form(@operation) 7 | end 8 | 9 | private def render_sign_in_form(op) 10 | form_for SignIns::Create do 11 | sign_in_fields(op) 12 | submit "Sign In", flow_id: "sign-in-button" 13 | end 14 | link "Reset password", to: PasswordResetRequests::New 15 | text " | " 16 | link "Sign up", to: SignUps::New 17 | end 18 | 19 | private def sign_in_fields(op) 20 | mount Shared::Field, attribute: op.email, label_text: "Email", &.email_input(autofocus: "true") 21 | mount Shared::Field, attribute: op.password, label_text: "Password", &.password_input 22 | end 23 | end 24 | -------------------------------------------------------------------------------- /fixtures/browser_authentication_src_template/expected/src/pages/sign_ups/new_page.cr: -------------------------------------------------------------------------------- 1 | class SignUps::NewPage < AuthLayout 2 | needs operation : SignUpUser 3 | 4 | def content 5 | h1 "Sign Up" 6 | render_sign_up_form(@operation) 7 | end 8 | 9 | private def render_sign_up_form(op) 10 | form_for SignUps::Create do 11 | sign_up_fields(op) 12 | submit "Sign Up", flow_id: "sign-up-button" 13 | end 14 | link "Sign in instead", to: SignIns::New 15 | end 16 | 17 | private def sign_up_fields(op) 18 | mount Shared::Field, attribute: op.email, label_text: "Email", &.email_input(autofocus: "true") 19 | mount Shared::Field, attribute: op.password, label_text: "Password", &.password_input 20 | mount Shared::Field, attribute: op.password_confirmation, label_text: "Confirm Password", &.password_input 21 | end 22 | end 23 | -------------------------------------------------------------------------------- /fixtures/browser_src_template/expected/bs-config.js: -------------------------------------------------------------------------------- 1 | /* 2 | | Browser-sync config file 3 | | 4 | | For up-to-date information about the options: 5 | | http://www.browsersync.io/docs/options/ 6 | | 7 | */ 8 | 9 | module.exports = { 10 | snippetOptions: { 11 | rule: { 12 | match: /<\/head>/i, 13 | fn: function (snippet, match) { 14 | return snippet + match; 15 | } 16 | } 17 | }, 18 | files: ["public/css/**/*.css", "public/js/**/*.js"], 19 | watchEvents: ["change"], 20 | open: false, 21 | browser: "default", 22 | ghostMode: false, 23 | ui: false, 24 | online: false, 25 | logConnections: false 26 | }; 27 | -------------------------------------------------------------------------------- /fixtures/browser_src_template/expected/config/html_page.cr: -------------------------------------------------------------------------------- 1 | Lucky::HTMLPage.configure do |settings| 2 | settings.render_component_comments = !LuckyEnv.production? 3 | end 4 | -------------------------------------------------------------------------------- /fixtures/browser_src_template/expected/db/migrations/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luckyframework/lucky_cli/ccaa571765582086d437efe0da4f439a19929d64/fixtures/browser_src_template/expected/db/migrations/.keep -------------------------------------------------------------------------------- /fixtures/browser_src_template/expected/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "license": "UNLICENSED", 3 | "private": true, 4 | "dependencies": { 5 | "@rails/ujs": "^7.1.0", 6 | "modern-normalize": "^2.0.0" 7 | }, 8 | "scripts": { 9 | "heroku-postbuild": "yarn prod", 10 | "dev": "yarn run mix", 11 | "watch": "yarn run mix watch", 12 | "prod": "yarn run mix --production" 13 | }, 14 | "devDependencies": { 15 | "@babel/compat-data": "^7.23.5", 16 | "browser-sync": "^2.29.3", 17 | "compression-webpack-plugin": "^10.0.0", 18 | "laravel-mix": "^6.0.49", 19 | "postcss": "^8.4.32", 20 | "resolve-url-loader": "^5.0.0", 21 | "sass": "^1.69.5", 22 | "sass-loader": "^13.3.2" 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /fixtures/browser_src_template/expected/public/assets/images/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luckyframework/lucky_cli/ccaa571765582086d437efe0da4f439a19929d64/fixtures/browser_src_template/expected/public/assets/images/.keep -------------------------------------------------------------------------------- /fixtures/browser_src_template/expected/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luckyframework/lucky_cli/ccaa571765582086d437efe0da4f439a19929d64/fixtures/browser_src_template/expected/public/favicon.ico -------------------------------------------------------------------------------- /fixtures/browser_src_template/expected/public/mix-manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "/css/app.css": "/css/app.css", 3 | "/js/app.js": "/js/app.js" 4 | } 5 | -------------------------------------------------------------------------------- /fixtures/browser_src_template/expected/public/robots.txt: -------------------------------------------------------------------------------- 1 | # Learn more about robots.txt: https://www.robotstxt.org/robotstxt.html 2 | User-agent: * 3 | # 'Disallow' with an empty value allows all paths to be crawled 4 | Disallow: 5 | -------------------------------------------------------------------------------- /fixtures/browser_src_template/expected/spec/flows/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luckyframework/lucky_cli/ccaa571765582086d437efe0da4f439a19929d64/fixtures/browser_src_template/expected/spec/flows/.keep -------------------------------------------------------------------------------- /fixtures/browser_src_template/expected/spec/setup/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luckyframework/lucky_cli/ccaa571765582086d437efe0da4f439a19929d64/fixtures/browser_src_template/expected/spec/setup/.keep -------------------------------------------------------------------------------- /fixtures/browser_src_template/expected/spec/support/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luckyframework/lucky_cli/ccaa571765582086d437efe0da4f439a19929d64/fixtures/browser_src_template/expected/spec/support/.keep -------------------------------------------------------------------------------- /fixtures/browser_src_template/expected/spec/support/factories/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luckyframework/lucky_cli/ccaa571765582086d437efe0da4f439a19929d64/fixtures/browser_src_template/expected/spec/support/factories/.keep -------------------------------------------------------------------------------- /fixtures/browser_src_template/expected/spec/support/flows/base_flow.cr: -------------------------------------------------------------------------------- 1 | # Add methods that all or most Flows need to share 2 | class BaseFlow < LuckyFlow 3 | end 4 | -------------------------------------------------------------------------------- /fixtures/browser_src_template/expected/src/components/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luckyframework/lucky_cli/ccaa571765582086d437efe0da4f439a19929d64/fixtures/browser_src_template/expected/src/components/.keep -------------------------------------------------------------------------------- /fixtures/browser_src_template/expected/src/components/base_component.cr: -------------------------------------------------------------------------------- 1 | abstract class BaseComponent < Lucky::BaseComponent 2 | end 3 | -------------------------------------------------------------------------------- /fixtures/browser_src_template/expected/src/components/shared/field_errors.cr: -------------------------------------------------------------------------------- 1 | class Shared::FieldErrors(T) < BaseComponent 2 | needs attribute : Avram::PermittedAttribute(T) 3 | 4 | # Customize the markup and styles to match your application 5 | def render 6 | unless attribute.valid? 7 | div class: "error" do 8 | text "#{label_text} #{attribute.errors.first}" 9 | end 10 | end 11 | end 12 | 13 | def label_text : String 14 | Wordsmith::Inflector.humanize(attribute.name.to_s) 15 | end 16 | end 17 | -------------------------------------------------------------------------------- /fixtures/browser_src_template/expected/src/components/shared/flash_messages.cr: -------------------------------------------------------------------------------- 1 | class Shared::FlashMessages < BaseComponent 2 | needs flash : Lucky::FlashStore 3 | 4 | def render 5 | flash.each do |flash_type, flash_message| 6 | div class: "flash-#{flash_type}", flow_id: "flash" do 7 | text flash_message 8 | end 9 | end 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /fixtures/browser_src_template/expected/src/components/shared/layout_head.cr: -------------------------------------------------------------------------------- 1 | class Shared::LayoutHead < BaseComponent 2 | needs page_title : String 3 | 4 | def render 5 | head do 6 | utf8_charset 7 | title "My App - #{@page_title}" 8 | css_link asset("css/app.css") 9 | js_link asset("js/app.js"), defer: "true" 10 | csrf_meta_tags 11 | responsive_meta_tag 12 | 13 | # Development helper used with the `lucky watch` command. 14 | # Reloads the browser when files are updated. 15 | live_reload_connect_tag if LuckyEnv.development? 16 | end 17 | end 18 | end 19 | -------------------------------------------------------------------------------- /fixtures/browser_src_template/expected/src/emails/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luckyframework/lucky_cli/ccaa571765582086d437efe0da4f439a19929d64/fixtures/browser_src_template/expected/src/emails/.keep -------------------------------------------------------------------------------- /fixtures/browser_src_template/expected/src/js/app.js: -------------------------------------------------------------------------------- 1 | /* eslint no-console:0 */ 2 | 3 | // Rails Unobtrusive JavaScript (UJS) is *required* for links in Lucky that use DELETE, POST and PUT. 4 | // Though it says "Rails" it actually works with any framework. 5 | import Rails from "@rails/ujs"; 6 | Rails.start(); 7 | 8 | -------------------------------------------------------------------------------- /fixtures/browser_src_template/expected/src/models/mixins/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luckyframework/lucky_cli/ccaa571765582086d437efe0da4f439a19929d64/fixtures/browser_src_template/expected/src/models/mixins/.keep -------------------------------------------------------------------------------- /fixtures/browser_src_template/expected/src/operations/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luckyframework/lucky_cli/ccaa571765582086d437efe0da4f439a19929d64/fixtures/browser_src_template/expected/src/operations/.keep -------------------------------------------------------------------------------- /fixtures/browser_src_template/expected/src/operations/mixins/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luckyframework/lucky_cli/ccaa571765582086d437efe0da4f439a19929d64/fixtures/browser_src_template/expected/src/operations/mixins/.keep -------------------------------------------------------------------------------- /fixtures/browser_src_template/expected/src/pages/main_layout.cr: -------------------------------------------------------------------------------- 1 | abstract class MainLayout 2 | include Lucky::HTMLPage 3 | 4 | abstract def content 5 | abstract def page_title 6 | 7 | # The default page title. It is passed to `Shared::LayoutHead`. 8 | # 9 | # Add a `page_title` method to pages to override it. You can also remove 10 | # This method so every page is required to have its own page title. 11 | def page_title 12 | "Welcome" 13 | end 14 | 15 | def render 16 | html_doctype 17 | 18 | html lang: "en" do 19 | mount Shared::LayoutHead, page_title: page_title 20 | 21 | body do 22 | mount Shared::FlashMessages, context.flash 23 | content 24 | end 25 | end 26 | end 27 | end 28 | -------------------------------------------------------------------------------- /fixtures/browser_src_template__generate_auth/expected/bs-config.js: -------------------------------------------------------------------------------- 1 | /* 2 | | Browser-sync config file 3 | | 4 | | For up-to-date information about the options: 5 | | http://www.browsersync.io/docs/options/ 6 | | 7 | */ 8 | 9 | module.exports = { 10 | snippetOptions: { 11 | rule: { 12 | match: /<\/head>/i, 13 | fn: function (snippet, match) { 14 | return snippet + match; 15 | } 16 | } 17 | }, 18 | files: ["public/css/**/*.css", "public/js/**/*.js"], 19 | watchEvents: ["change"], 20 | open: false, 21 | browser: "default", 22 | ghostMode: false, 23 | ui: false, 24 | online: false, 25 | logConnections: false 26 | }; 27 | -------------------------------------------------------------------------------- /fixtures/browser_src_template__generate_auth/expected/config/html_page.cr: -------------------------------------------------------------------------------- 1 | Lucky::HTMLPage.configure do |settings| 2 | settings.render_component_comments = !LuckyEnv.production? 3 | end 4 | -------------------------------------------------------------------------------- /fixtures/browser_src_template__generate_auth/expected/db/migrations/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luckyframework/lucky_cli/ccaa571765582086d437efe0da4f439a19929d64/fixtures/browser_src_template__generate_auth/expected/db/migrations/.keep -------------------------------------------------------------------------------- /fixtures/browser_src_template__generate_auth/expected/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "license": "UNLICENSED", 3 | "private": true, 4 | "dependencies": { 5 | "@rails/ujs": "^7.1.0", 6 | "modern-normalize": "^2.0.0" 7 | }, 8 | "scripts": { 9 | "heroku-postbuild": "yarn prod", 10 | "dev": "yarn run mix", 11 | "watch": "yarn run mix watch", 12 | "prod": "yarn run mix --production" 13 | }, 14 | "devDependencies": { 15 | "@babel/compat-data": "^7.23.5", 16 | "browser-sync": "^2.29.3", 17 | "compression-webpack-plugin": "^10.0.0", 18 | "laravel-mix": "^6.0.49", 19 | "postcss": "^8.4.32", 20 | "resolve-url-loader": "^5.0.0", 21 | "sass": "^1.69.5", 22 | "sass-loader": "^13.3.2" 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /fixtures/browser_src_template__generate_auth/expected/public/assets/images/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luckyframework/lucky_cli/ccaa571765582086d437efe0da4f439a19929d64/fixtures/browser_src_template__generate_auth/expected/public/assets/images/.keep -------------------------------------------------------------------------------- /fixtures/browser_src_template__generate_auth/expected/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luckyframework/lucky_cli/ccaa571765582086d437efe0da4f439a19929d64/fixtures/browser_src_template__generate_auth/expected/public/favicon.ico -------------------------------------------------------------------------------- /fixtures/browser_src_template__generate_auth/expected/public/mix-manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "/css/app.css": "/css/app.css", 3 | "/js/app.js": "/js/app.js" 4 | } 5 | -------------------------------------------------------------------------------- /fixtures/browser_src_template__generate_auth/expected/public/robots.txt: -------------------------------------------------------------------------------- 1 | # Learn more about robots.txt: https://www.robotstxt.org/robotstxt.html 2 | User-agent: * 3 | # 'Disallow' with an empty value allows all paths to be crawled 4 | Disallow: 5 | -------------------------------------------------------------------------------- /fixtures/browser_src_template__generate_auth/expected/spec/flows/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luckyframework/lucky_cli/ccaa571765582086d437efe0da4f439a19929d64/fixtures/browser_src_template__generate_auth/expected/spec/flows/.keep -------------------------------------------------------------------------------- /fixtures/browser_src_template__generate_auth/expected/spec/setup/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luckyframework/lucky_cli/ccaa571765582086d437efe0da4f439a19929d64/fixtures/browser_src_template__generate_auth/expected/spec/setup/.keep -------------------------------------------------------------------------------- /fixtures/browser_src_template__generate_auth/expected/spec/support/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luckyframework/lucky_cli/ccaa571765582086d437efe0da4f439a19929d64/fixtures/browser_src_template__generate_auth/expected/spec/support/.keep -------------------------------------------------------------------------------- /fixtures/browser_src_template__generate_auth/expected/spec/support/factories/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luckyframework/lucky_cli/ccaa571765582086d437efe0da4f439a19929d64/fixtures/browser_src_template__generate_auth/expected/spec/support/factories/.keep -------------------------------------------------------------------------------- /fixtures/browser_src_template__generate_auth/expected/spec/support/flows/base_flow.cr: -------------------------------------------------------------------------------- 1 | # Add methods that all or most Flows need to share 2 | class BaseFlow < LuckyFlow 3 | end 4 | -------------------------------------------------------------------------------- /fixtures/browser_src_template__generate_auth/expected/src/actions/browser_action.cr: -------------------------------------------------------------------------------- 1 | abstract class BrowserAction < Lucky::Action 2 | include Lucky::ProtectFromForgery 3 | 4 | # By default all actions are required to use underscores. 5 | # Add `include Lucky::SkipRouteStyleCheck` to your actions if you wish to ignore this check for specific routes. 6 | include Lucky::EnforceUnderscoredRoute 7 | 8 | # This module disables Google FLoC by setting the 9 | # [Permissions-Policy](https://github.com/WICG/floc) HTTP header to `interest-cohort=()`. 10 | # 11 | # This header is a part of Google's Federated Learning of Cohorts (FLoC) which is used 12 | # to track browsing history instead of using 3rd-party cookies. 13 | # 14 | # Remove this include if you want to use the FLoC tracking. 15 | include Lucky::SecureHeaders::DisableFLoC 16 | 17 | accepted_formats [:html, :json], default: :html 18 | end 19 | -------------------------------------------------------------------------------- /fixtures/browser_src_template__generate_auth/expected/src/components/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luckyframework/lucky_cli/ccaa571765582086d437efe0da4f439a19929d64/fixtures/browser_src_template__generate_auth/expected/src/components/.keep -------------------------------------------------------------------------------- /fixtures/browser_src_template__generate_auth/expected/src/components/base_component.cr: -------------------------------------------------------------------------------- 1 | abstract class BaseComponent < Lucky::BaseComponent 2 | end 3 | -------------------------------------------------------------------------------- /fixtures/browser_src_template__generate_auth/expected/src/components/shared/field_errors.cr: -------------------------------------------------------------------------------- 1 | class Shared::FieldErrors(T) < BaseComponent 2 | needs attribute : Avram::PermittedAttribute(T) 3 | 4 | # Customize the markup and styles to match your application 5 | def render 6 | unless attribute.valid? 7 | div class: "error" do 8 | text "#{label_text} #{attribute.errors.first}" 9 | end 10 | end 11 | end 12 | 13 | def label_text : String 14 | Wordsmith::Inflector.humanize(attribute.name.to_s) 15 | end 16 | end 17 | -------------------------------------------------------------------------------- /fixtures/browser_src_template__generate_auth/expected/src/components/shared/flash_messages.cr: -------------------------------------------------------------------------------- 1 | class Shared::FlashMessages < BaseComponent 2 | needs flash : Lucky::FlashStore 3 | 4 | def render 5 | flash.each do |flash_type, flash_message| 6 | div class: "flash-#{flash_type}", flow_id: "flash" do 7 | text flash_message 8 | end 9 | end 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /fixtures/browser_src_template__generate_auth/expected/src/components/shared/layout_head.cr: -------------------------------------------------------------------------------- 1 | class Shared::LayoutHead < BaseComponent 2 | needs page_title : String 3 | 4 | def render 5 | head do 6 | utf8_charset 7 | title "My App - #{@page_title}" 8 | css_link asset("css/app.css") 9 | js_link asset("js/app.js"), defer: "true" 10 | csrf_meta_tags 11 | responsive_meta_tag 12 | 13 | # Development helper used with the `lucky watch` command. 14 | # Reloads the browser when files are updated. 15 | live_reload_connect_tag if LuckyEnv.development? 16 | end 17 | end 18 | end 19 | -------------------------------------------------------------------------------- /fixtures/browser_src_template__generate_auth/expected/src/emails/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luckyframework/lucky_cli/ccaa571765582086d437efe0da4f439a19929d64/fixtures/browser_src_template__generate_auth/expected/src/emails/.keep -------------------------------------------------------------------------------- /fixtures/browser_src_template__generate_auth/expected/src/js/app.js: -------------------------------------------------------------------------------- 1 | /* eslint no-console:0 */ 2 | 3 | // Rails Unobtrusive JavaScript (UJS) is *required* for links in Lucky that use DELETE, POST and PUT. 4 | // Though it says "Rails" it actually works with any framework. 5 | import Rails from "@rails/ujs"; 6 | Rails.start(); 7 | 8 | -------------------------------------------------------------------------------- /fixtures/browser_src_template__generate_auth/expected/src/models/mixins/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luckyframework/lucky_cli/ccaa571765582086d437efe0da4f439a19929d64/fixtures/browser_src_template__generate_auth/expected/src/models/mixins/.keep -------------------------------------------------------------------------------- /fixtures/browser_src_template__generate_auth/expected/src/operations/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luckyframework/lucky_cli/ccaa571765582086d437efe0da4f439a19929d64/fixtures/browser_src_template__generate_auth/expected/src/operations/.keep -------------------------------------------------------------------------------- /fixtures/browser_src_template__generate_auth/expected/src/operations/mixins/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luckyframework/lucky_cli/ccaa571765582086d437efe0da4f439a19929d64/fixtures/browser_src_template__generate_auth/expected/src/operations/mixins/.keep -------------------------------------------------------------------------------- /fixtures/browser_src_template__generate_auth/expected/src/pages/main_layout.cr: -------------------------------------------------------------------------------- 1 | abstract class MainLayout 2 | include Lucky::HTMLPage 3 | 4 | abstract def content 5 | abstract def page_title 6 | 7 | # The default page title. It is passed to `Shared::LayoutHead`. 8 | # 9 | # Add a `page_title` method to pages to override it. You can also remove 10 | # This method so every page is required to have its own page title. 11 | def page_title 12 | "Welcome" 13 | end 14 | 15 | def render 16 | html_doctype 17 | 18 | html lang: "en" do 19 | mount Shared::LayoutHead, page_title: page_title 20 | 21 | body do 22 | mount Shared::FlashMessages, context.flash 23 | content 24 | end 25 | end 26 | end 27 | end 28 | -------------------------------------------------------------------------------- /fixtures/cat.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luckyframework/lucky_cli/ccaa571765582086d437efe0da4f439a19929d64/fixtures/cat.gif -------------------------------------------------------------------------------- /fixtures/hello_crystal.cr: -------------------------------------------------------------------------------- 1 | puts "Hello, Crystal!" 2 | -------------------------------------------------------------------------------- /fixtures/hello_world.cr: -------------------------------------------------------------------------------- 1 | puts "Hello World!" 2 | -------------------------------------------------------------------------------- /fixtures/shard_file_template__browser/expected/shard.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: test-shard 3 | version: 0.1.0 4 | targets: 5 | test-shard: 6 | main: src/test-shard.cr 7 | crystal: '>= 1.16.1' 8 | dependencies: 9 | lucky: 10 | github: luckyframework/lucky 11 | version: ~> 1.4.0 12 | avram: 13 | github: luckyframework/avram 14 | version: ~> 1.4.0 15 | carbon: 16 | github: luckyframework/carbon 17 | version: ~> 0.6.0 18 | carbon_sendgrid_adapter: 19 | github: luckyframework/carbon_sendgrid_adapter 20 | version: ~> 0.6.0 21 | lucky_env: 22 | github: luckyframework/lucky_env 23 | version: ~> 0.3.0 24 | lucky_task: 25 | github: luckyframework/lucky_task 26 | version: ~> 0.3.0 27 | development_dependencies: 28 | lucky_flow: 29 | github: luckyframework/lucky_flow 30 | version: ~> 0.10.0 31 | -------------------------------------------------------------------------------- /fixtures/shard_file_template__generate_auth/expected/shard.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: test-shard 3 | version: 0.1.0 4 | targets: 5 | test-shard: 6 | main: src/test-shard.cr 7 | crystal: '>= 1.16.1' 8 | dependencies: 9 | lucky: 10 | github: luckyframework/lucky 11 | version: ~> 1.4.0 12 | avram: 13 | github: luckyframework/avram 14 | version: ~> 1.4.0 15 | carbon: 16 | github: luckyframework/carbon 17 | version: ~> 0.6.0 18 | carbon_sendgrid_adapter: 19 | github: luckyframework/carbon_sendgrid_adapter 20 | version: ~> 0.6.0 21 | lucky_env: 22 | github: luckyframework/lucky_env 23 | version: ~> 0.3.0 24 | lucky_task: 25 | github: luckyframework/lucky_task 26 | version: ~> 0.3.0 27 | authentic: 28 | github: luckyframework/authentic 29 | version: '>= 1.0.2, < 2.0.0' 30 | jwt: 31 | github: crystal-community/jwt 32 | version: ~> 1.6.1 33 | development_dependencies: {} 34 | -------------------------------------------------------------------------------- /fixtures/shard_file_template__with_sec_tester/expected/shard.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: test-shard 3 | version: 0.1.0 4 | targets: 5 | test-shard: 6 | main: src/test-shard.cr 7 | crystal: '>= 1.16.1' 8 | dependencies: 9 | lucky: 10 | github: luckyframework/lucky 11 | version: ~> 1.4.0 12 | avram: 13 | github: luckyframework/avram 14 | version: ~> 1.4.0 15 | carbon: 16 | github: luckyframework/carbon 17 | version: ~> 0.6.0 18 | carbon_sendgrid_adapter: 19 | github: luckyframework/carbon_sendgrid_adapter 20 | version: ~> 0.6.0 21 | lucky_env: 22 | github: luckyframework/lucky_env 23 | version: ~> 0.3.0 24 | lucky_task: 25 | github: luckyframework/lucky_task 26 | version: ~> 0.3.0 27 | development_dependencies: 28 | lucky_sec_tester: 29 | github: luckyframework/lucky_sec_tester 30 | version: ~> 0.3.3 31 | -------------------------------------------------------------------------------- /fixtures/src_template/expected/.crystal-version: -------------------------------------------------------------------------------- 1 | 1.16.1 2 | -------------------------------------------------------------------------------- /fixtures/src_template/expected/.env: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luckyframework/lucky_cli/ccaa571765582086d437efe0da4f439a19929d64/fixtures/src_template/expected/.env -------------------------------------------------------------------------------- /fixtures/src_template/expected/Procfile: -------------------------------------------------------------------------------- 1 | web: bin/app 2 | release: lucky db.migrate 3 | -------------------------------------------------------------------------------- /fixtures/src_template/expected/Procfile.dev: -------------------------------------------------------------------------------- 1 | system_check: crystal script/system_check.cr 2 | web: lucky watch 3 | -------------------------------------------------------------------------------- /fixtures/src_template/expected/README.md: -------------------------------------------------------------------------------- 1 | # test-project 2 | 3 | This is a project written using [Lucky](https://luckyframework.org). Enjoy! 4 | 5 | ### Setting up the project 6 | 7 | 1. [Install required dependencies](https://luckyframework.org/guides/getting-started/installing#install-required-dependencies) 8 | 1. Update database settings in `config/database.cr` 9 | 1. Run `script/setup` 10 | 1. Run `lucky dev` to start the app 11 | 12 | ### Using Docker for development 13 | 14 | 1. [Install Docker](https://docs.docker.com/engine/install/) 15 | 1. Run `docker compose up` 16 | 17 | The Docker container will boot all of the necessary components needed to run your Lucky application. 18 | To configure the container, update the `docker-compose.yml` file, and the `docker/development.dockerfile` file. 19 | 20 | 21 | ### Learning Lucky 22 | 23 | Lucky uses the [Crystal](https://crystal-lang.org) programming language. You can learn about Lucky from the [Lucky Guides](https://luckyframework.org/guides/getting-started/why-lucky). 24 | -------------------------------------------------------------------------------- /fixtures/src_template/expected/config/application.cr: -------------------------------------------------------------------------------- 1 | # This file may be used for custom Application configurations. 2 | # It will be loaded before other config files. 3 | # 4 | # Read more on configuration: 5 | # https://luckyframework.org/guides/getting-started/configuration#configuring-your-own-code 6 | 7 | # Use this code as an example: 8 | # 9 | # ``` 10 | # module Application 11 | # Habitat.create do 12 | # setting support_email : String 13 | # setting lock_with_basic_auth : Bool 14 | # end 15 | # end 16 | # 17 | # Application.configure do |settings| 18 | # settings.support_email = "support@myapp.io" 19 | # settings.lock_with_basic_auth = LuckyEnv.staging? 20 | # end 21 | # 22 | # # In your application, call 23 | # # `Application.settings.support_email` anywhere you need it. 24 | # ``` 25 | -------------------------------------------------------------------------------- /fixtures/src_template/expected/config/colors.cr: -------------------------------------------------------------------------------- 1 | # This enables the color output when in development or test 2 | # Check out the Colorize docs for more information 3 | # https://crystal-lang.org/api/Colorize.html 4 | Colorize.enabled = LuckyEnv.development? || LuckyEnv.test? 5 | -------------------------------------------------------------------------------- /fixtures/src_template/expected/config/cookies.cr: -------------------------------------------------------------------------------- 1 | require "./server" 2 | 3 | Lucky::Session.configure do |settings| 4 | settings.key = "_test_project_session" 5 | end 6 | 7 | Lucky::CookieJar.configure do |settings| 8 | settings.on_set = ->(cookie : HTTP::Cookie) { 9 | # If ForceSSLHandler is enabled, only send cookies over HTTPS 10 | cookie.secure(Lucky::ForceSSLHandler.settings.enabled) 11 | 12 | # By default, don't allow reading cookies with JavaScript 13 | cookie.http_only(true) 14 | 15 | # Restrict cookies to a first-party or same-site context 16 | cookie.samesite(:lax) 17 | 18 | # Set all cookies to the root path by default 19 | cookie.path("/") 20 | 21 | # You can set other defaults for cookies here. For example: 22 | # 23 | # cookie.expires(1.year.from_now).domain("mydomain.com") 24 | } 25 | end 26 | -------------------------------------------------------------------------------- /fixtures/src_template/expected/config/env.cr: -------------------------------------------------------------------------------- 1 | # Environments are managed using `LuckyEnv`. By default, development, production 2 | # and test are supported. See 3 | # https://luckyframework.org/guides/getting-started/configuration for details. 4 | # 5 | # The default environment is development unless the environment variable 6 | # LUCKY_ENV is set. 7 | # 8 | # Example: 9 | # ``` 10 | # LuckyEnv.environment # => "development" 11 | # LuckyEnv.development? # => true 12 | # LuckyEnv.production? # => false 13 | # LuckyEnv.test? # => false 14 | # ``` 15 | # 16 | # New environments can be added using the `LuckyEnv.add_env` macro. 17 | # 18 | # Example: 19 | # ``` 20 | # LuckyEnv.add_env :staging 21 | # LuckyEnv.staging? # => false 22 | # ``` 23 | # 24 | # To determine whether or not a `LuckyTask` is currently running, you can use 25 | # the `LuckyEnv.task?` predicate. 26 | # 27 | # Example: 28 | # ``` 29 | # LuckyEnv.task? # => false 30 | # ``` 31 | 32 | # Add a staging environment. 33 | # LuckyEnv.add_env :staging 34 | -------------------------------------------------------------------------------- /fixtures/src_template/expected/config/error_handler.cr: -------------------------------------------------------------------------------- 1 | Lucky::ErrorHandler.configure do |settings| 2 | settings.show_debug_output = !LuckyEnv.production? 3 | end 4 | -------------------------------------------------------------------------------- /fixtures/src_template/expected/config/route_helper.cr: -------------------------------------------------------------------------------- 1 | # This is used when generating URLs for your application 2 | Lucky::RouteHelper.configure do |settings| 3 | if LuckyEnv.production? 4 | # Example: https://my_app.com 5 | settings.base_uri = ENV.fetch("APP_DOMAIN") 6 | else 7 | # Set domain to the default host/port in development/test 8 | settings.base_uri = "http://localhost:#{Lucky::ServerSettings.port}" 9 | end 10 | end 11 | -------------------------------------------------------------------------------- /fixtures/src_template/expected/config/watch.yml: -------------------------------------------------------------------------------- 1 | host: 127.0.0.1 2 | port: 3000 3 | reload_port: 3001 4 | -------------------------------------------------------------------------------- /fixtures/src_template/expected/db/migrations/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luckyframework/lucky_cli/ccaa571765582086d437efe0da4f439a19929d64/fixtures/src_template/expected/db/migrations/.keep -------------------------------------------------------------------------------- /fixtures/src_template/expected/docker/development.dockerfile: -------------------------------------------------------------------------------- 1 | FROM crystallang/crystal:1.16.1 2 | 3 | # Install utilities required to make this Dockerfile run 4 | RUN apt-get update && \ 5 | apt-get install -y wget 6 | 7 | # Apt installs: 8 | # - Postgres cli tools are required for lucky-cli. 9 | # - tmux is required for the Overmind process manager. 10 | RUN apt-get update && \ 11 | apt-get install -y postgresql-client tmux && \ 12 | rm -rf /var/lib/apt/lists/* 13 | 14 | # Install lucky cli 15 | WORKDIR /lucky/cli 16 | RUN git clone https://github.com/luckyframework/lucky_cli . && \ 17 | git checkout v1.3.0 && \ 18 | shards build --without-development && \ 19 | cp bin/lucky /usr/bin 20 | 21 | WORKDIR /app 22 | ENV DATABASE_URL=postgres://postgres:postgres@host.docker.internal:5432/postgres 23 | EXPOSE 3000 24 | EXPOSE 3001 25 | 26 | -------------------------------------------------------------------------------- /fixtures/src_template/expected/script/helpers/function_helpers.cr: -------------------------------------------------------------------------------- 1 | require "colorize" 2 | 3 | # These are helper methods provided to help keep your code 4 | # clean. Add new methods, or alter these as needed. 5 | 6 | def notice(message : String) : Nil 7 | puts "\n▸ #{message}" 8 | end 9 | 10 | def print_done : Nil 11 | puts "✔ Done" 12 | end 13 | 14 | def print_error(message : String) : Nil 15 | puts "There is a problem with your system setup:\n".colorize.red.bold 16 | puts "#{message}\n".colorize.red.bold 17 | Process.exit(1) 18 | end 19 | 20 | def command_not_found(command : String) : Bool 21 | Process.find_executable(command).nil? 22 | end 23 | 24 | def command_not_running(command : String, *args) : Bool 25 | output = IO::Memory.new 26 | code = Process.run(command, args, output: output).exit_code 27 | code > 0 28 | end 29 | 30 | def run_command(command : String, *args) : Nil 31 | Process.run(command, args, output: STDOUT, error: STDERR, input: STDIN) 32 | end 33 | -------------------------------------------------------------------------------- /fixtures/src_template/expected/script/setup.cr: -------------------------------------------------------------------------------- 1 | require "./helpers/*" 2 | 3 | notice "Running System Check" 4 | 5 | require "./system_check" 6 | 7 | print_done 8 | 9 | 10 | notice "Installing shards" 11 | run_command "shards", "install" 12 | 13 | if !File.exists?(".env") 14 | notice "No .env found. Creating one." 15 | File.touch ".env" 16 | print_done 17 | end 18 | 19 | notice "Setting up the database" 20 | 21 | run_command "lucky", "db.setup" 22 | 23 | notice "Seeding the database with required and sample records" 24 | run_command "lucky", "db.seed.required_data" 25 | run_command "lucky", "db.seed.sample_data" 26 | 27 | print_done 28 | notice "Run 'lucky dev' to start the app" 29 | -------------------------------------------------------------------------------- /fixtures/src_template/expected/script/system_check.cr: -------------------------------------------------------------------------------- 1 | require "./helpers/*" 2 | 3 | # Use this script to check the system for required tools and process that your app needs. 4 | # A few helper functions are provided to keep the code simple. See the 5 | # script/helpers/function_helpers.cr file for more examples. 6 | # 7 | # A few examples you might use here: 8 | # * 'lucky db.verify_connection' to test postgres can be connected 9 | # * Checking that elasticsearch, redis, or postgres is installed and/or booted 10 | # * Note: Booting additional processes for things like mail, background jobs, etc... 11 | # should go in your Procfile.dev. 12 | 13 | 14 | # CUSTOM PRE-BOOT CHECKS 15 | # example: 16 | # if command_not_running "redis-cli", "ping" 17 | # print_error "Redis is not running." 18 | # end 19 | -------------------------------------------------------------------------------- /fixtures/src_template/expected/spec/setup/clean_database.cr: -------------------------------------------------------------------------------- 1 | Spec.before_each do 2 | AppDatabase.truncate 3 | end 4 | -------------------------------------------------------------------------------- /fixtures/src_template/expected/spec/setup/reset_emails.cr: -------------------------------------------------------------------------------- 1 | Spec.before_each do 2 | Carbon::DevAdapter.reset 3 | end 4 | -------------------------------------------------------------------------------- /fixtures/src_template/expected/spec/setup/setup_database.cr: -------------------------------------------------------------------------------- 1 | Db::Create.new(quiet: true).call 2 | Db::Migrate.new(quiet: true).call 3 | -------------------------------------------------------------------------------- /fixtures/src_template/expected/spec/setup/start_app_server.cr: -------------------------------------------------------------------------------- 1 | app_server = AppServer.new 2 | 3 | spawn do 4 | app_server.listen 5 | end 6 | 7 | Spec.after_suite do 8 | app_server.close 9 | end 10 | -------------------------------------------------------------------------------- /fixtures/src_template/expected/spec/spec_helper.cr: -------------------------------------------------------------------------------- 1 | ENV["LUCKY_ENV"] = "test" 2 | ENV["DEV_PORT"] = "5001" 3 | require "spec" 4 | require "../src/app" 5 | require "./support/**" 6 | require "../db/migrations/**" 7 | 8 | # Add/modify files in spec/setup to start/configure programs or run hooks 9 | # 10 | # By default there are scripts for setting up and cleaning the database, 11 | # configuring LuckyFlow, starting the app server, etc. 12 | require "./setup/**" 13 | 14 | include Carbon::Expectations 15 | include Lucky::RequestExpectations 16 | 17 | Avram::Migrator::Runner.new.ensure_migrated! 18 | Avram::SchemaEnforcer.ensure_correct_column_mappings! 19 | Habitat.raise_if_missing_settings! 20 | -------------------------------------------------------------------------------- /fixtures/src_template/expected/spec/support/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luckyframework/lucky_cli/ccaa571765582086d437efe0da4f439a19929d64/fixtures/src_template/expected/spec/support/.keep -------------------------------------------------------------------------------- /fixtures/src_template/expected/spec/support/api_client.cr: -------------------------------------------------------------------------------- 1 | class ApiClient < Lucky::BaseHTTPClient 2 | app AppServer.new 3 | 4 | def initialize 5 | super 6 | headers("Content-Type": "application/json") 7 | end 8 | 9 | def self.auth(user : User) 10 | new.headers("Authorization": UserToken.generate(user)) 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /fixtures/src_template/expected/spec/support/factories/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luckyframework/lucky_cli/ccaa571765582086d437efe0da4f439a19929d64/fixtures/src_template/expected/spec/support/factories/.keep -------------------------------------------------------------------------------- /fixtures/src_template/expected/src/actions/api_action.cr: -------------------------------------------------------------------------------- 1 | # Include modules and add methods that are for all API requests 2 | abstract class ApiAction < Lucky::Action 3 | # APIs typically do not need to send cookie/session data. 4 | # Remove this line if you want to send cookies in the response header. 5 | disable_cookies 6 | accepted_formats [:json] 7 | 8 | include Api::Auth::Helpers 9 | 10 | # By default all actions require sign in. 11 | # Add 'include Api::Auth::SkipRequireAuthToken' to your actions to allow all requests. 12 | include Api::Auth::RequireAuthToken 13 | 14 | # By default all actions are required to use underscores to separate words. 15 | # Add 'include Lucky::SkipRouteStyleCheck' to your actions if you wish to ignore this check for specific routes. 16 | include Lucky::EnforceUnderscoredRoute 17 | end 18 | -------------------------------------------------------------------------------- /fixtures/src_template/expected/src/actions/home/index.cr: -------------------------------------------------------------------------------- 1 | class Home::Index < ApiAction 2 | include Api::Auth::SkipRequireAuthToken 3 | 4 | get "/" do 5 | json({hello: "Hello World from Home::Index"}) 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /fixtures/src_template/expected/src/actions/mixins/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luckyframework/lucky_cli/ccaa571765582086d437efe0da4f439a19929d64/fixtures/src_template/expected/src/actions/mixins/.keep -------------------------------------------------------------------------------- /fixtures/src_template/expected/src/app.cr: -------------------------------------------------------------------------------- 1 | require "./shards" 2 | 3 | require "../config/server" 4 | require "./app_database" 5 | require "../config/**" 6 | require "./models/base_model" 7 | require "./models/mixins/**" 8 | require "./models/**" 9 | require "./queries/mixins/**" 10 | require "./queries/**" 11 | require "./operations/mixins/**" 12 | require "./operations/**" 13 | require "./serializers/base_serializer" 14 | require "./serializers/**" 15 | require "./emails/base_email" 16 | require "./emails/**" 17 | require "./actions/mixins/**" 18 | require "./actions/**" 19 | require "../db/migrations/**" 20 | require "./app_server" 21 | -------------------------------------------------------------------------------- /fixtures/src_template/expected/src/app_database.cr: -------------------------------------------------------------------------------- 1 | class AppDatabase < Avram::Database 2 | end 3 | -------------------------------------------------------------------------------- /fixtures/src_template/expected/src/app_server.cr: -------------------------------------------------------------------------------- 1 | class AppServer < Lucky::BaseAppServer 2 | # Learn about middleware with HTTP::Handlers: 3 | # https://luckyframework.org/guides/http-and-routing/http-handlers 4 | def middleware : Array(HTTP::Handler) 5 | [ 6 | Lucky::RequestIdHandler.new, 7 | Lucky::ForceSSLHandler.new, 8 | Lucky::HttpMethodOverrideHandler.new, 9 | Lucky::LogHandler.new, 10 | Lucky::ErrorHandler.new(action: Errors::Show), 11 | Lucky::RemoteIpHandler.new, 12 | Lucky::RouteHandler.new, 13 | 14 | # Disabled in API mode: 15 | # Lucky::StaticCompressionHandler.new("./public", file_ext: "gz", content_encoding: "gzip"), 16 | # Lucky::StaticFileHandler.new("./public", fallthrough: false, directory_listing: false), 17 | Lucky::RouteNotFoundHandler.new, 18 | ] of HTTP::Handler 19 | end 20 | 21 | def protocol 22 | "http" 23 | end 24 | 25 | def listen 26 | server.listen(host, port, reuse_port: false) 27 | end 28 | end 29 | -------------------------------------------------------------------------------- /fixtures/src_template/expected/src/emails/base_email.cr: -------------------------------------------------------------------------------- 1 | # Learn about sending emails 2 | # https://luckyframework.org/guides/emails/sending-emails-with-carbon 3 | abstract class BaseEmail < Carbon::Email 4 | # You can add defaults using the 'inherited' hook 5 | # 6 | # Example: 7 | # 8 | # macro inherited 9 | # from default_from 10 | # end 11 | # 12 | # def default_from 13 | # Carbon::Address.new("support@app.com") 14 | # end 15 | end 16 | -------------------------------------------------------------------------------- /fixtures/src_template/expected/src/models/base_model.cr: -------------------------------------------------------------------------------- 1 | abstract class BaseModel < Avram::Model 2 | def self.database : Avram::Database.class 3 | AppDatabase 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /fixtures/src_template/expected/src/models/mixins/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luckyframework/lucky_cli/ccaa571765582086d437efe0da4f439a19929d64/fixtures/src_template/expected/src/models/mixins/.keep -------------------------------------------------------------------------------- /fixtures/src_template/expected/src/operations/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luckyframework/lucky_cli/ccaa571765582086d437efe0da4f439a19929d64/fixtures/src_template/expected/src/operations/.keep -------------------------------------------------------------------------------- /fixtures/src_template/expected/src/operations/mixins/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luckyframework/lucky_cli/ccaa571765582086d437efe0da4f439a19929d64/fixtures/src_template/expected/src/operations/mixins/.keep -------------------------------------------------------------------------------- /fixtures/src_template/expected/src/queries/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luckyframework/lucky_cli/ccaa571765582086d437efe0da4f439a19929d64/fixtures/src_template/expected/src/queries/.keep -------------------------------------------------------------------------------- /fixtures/src_template/expected/src/queries/mixins/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luckyframework/lucky_cli/ccaa571765582086d437efe0da4f439a19929d64/fixtures/src_template/expected/src/queries/mixins/.keep -------------------------------------------------------------------------------- /fixtures/src_template/expected/src/serializers/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luckyframework/lucky_cli/ccaa571765582086d437efe0da4f439a19929d64/fixtures/src_template/expected/src/serializers/.keep -------------------------------------------------------------------------------- /fixtures/src_template/expected/src/serializers/base_serializer.cr: -------------------------------------------------------------------------------- 1 | abstract class BaseSerializer 2 | include Lucky::Serializable 3 | 4 | def self.for_collection(collection : Enumerable, *args, **named_args) : Array(self) 5 | collection.map do |object| 6 | new(object, *args, **named_args) 7 | end 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /fixtures/src_template/expected/src/serializers/error_serializer.cr: -------------------------------------------------------------------------------- 1 | # This is the default error serializer generated by Lucky. 2 | # Feel free to customize it in any way you like. 3 | class ErrorSerializer < BaseSerializer 4 | def initialize( 5 | @message : String, 6 | @details : String? = nil, 7 | @param : String? = nil, # so you can track which param (if any) caused the problem 8 | ) 9 | end 10 | 11 | def render 12 | {message: @message, param: @param, details: @details} 13 | end 14 | end 15 | -------------------------------------------------------------------------------- /fixtures/src_template/expected/src/shards.cr: -------------------------------------------------------------------------------- 1 | # Load .env file before any other config or app code 2 | require "lucky_env" 3 | LuckyEnv.load?(".env") 4 | 5 | # Require your shards here 6 | require "lucky" 7 | require "avram/lucky" 8 | require "carbon" 9 | require "authentic" 10 | require "jwt" 11 | -------------------------------------------------------------------------------- /fixtures/src_template/expected/src/start_server.cr: -------------------------------------------------------------------------------- 1 | require "./app" 2 | 3 | Habitat.raise_if_missing_settings! 4 | 5 | if LuckyEnv.development? 6 | Avram::Migrator::Runner.new.ensure_migrated! 7 | Avram::SchemaEnforcer.ensure_correct_column_mappings! 8 | end 9 | 10 | app_server = AppServer.new 11 | puts "Listening on http://#{app_server.host}:#{app_server.port}" 12 | 13 | Signal::INT.trap do 14 | app_server.close 15 | end 16 | 17 | app_server.listen 18 | -------------------------------------------------------------------------------- /fixtures/src_template/expected/src/test_project.cr: -------------------------------------------------------------------------------- 1 | # Typically you will not use or modify this file. 'shards build' and some 2 | # other crystal tools will sometimes use this. 3 | # 4 | # When this file is compiled/run it will require and run 'start_server', 5 | # which as its name implies will start the server for you app. 6 | require "./start_server" 7 | -------------------------------------------------------------------------------- /fixtures/src_template/expected/tasks.cr: -------------------------------------------------------------------------------- 1 | # This file loads your app and all your tasks when running 'lucky' 2 | # 3 | # Run 'lucky --help' to see all available tasks. 4 | # 5 | # Learn to create your own tasks: 6 | # https://luckyframework.org/guides/command-line-tasks/custom-tasks 7 | 8 | # See `LuckyEnv#task?` 9 | ENV["LUCKY_TASK"] = "true" 10 | 11 | # Load Lucky and the app (actions, models, etc.) 12 | require "./src/app" 13 | require "lucky_task" 14 | 15 | # You can add your own tasks here in the ./tasks folder 16 | require "./tasks/**" 17 | 18 | # Load migrations 19 | require "./db/migrations/**" 20 | 21 | # Load Lucky tasks (dev, routes, etc.) 22 | require "lucky/tasks/**" 23 | require "avram/lucky/tasks" 24 | 25 | LuckyTask::Runner.run 26 | -------------------------------------------------------------------------------- /fixtures/src_template/expected/tasks/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luckyframework/lucky_cli/ccaa571765582086d437efe0da4f439a19929d64/fixtures/src_template/expected/tasks/.keep -------------------------------------------------------------------------------- /fixtures/src_template__api_only/expected/.crystal-version: -------------------------------------------------------------------------------- 1 | 1.16.1 2 | -------------------------------------------------------------------------------- /fixtures/src_template__api_only/expected/.env: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luckyframework/lucky_cli/ccaa571765582086d437efe0da4f439a19929d64/fixtures/src_template__api_only/expected/.env -------------------------------------------------------------------------------- /fixtures/src_template__api_only/expected/Procfile: -------------------------------------------------------------------------------- 1 | web: bin/app 2 | release: lucky db.migrate 3 | -------------------------------------------------------------------------------- /fixtures/src_template__api_only/expected/Procfile.dev: -------------------------------------------------------------------------------- 1 | system_check: crystal script/system_check.cr 2 | web: lucky watch 3 | -------------------------------------------------------------------------------- /fixtures/src_template__api_only/expected/config/application.cr: -------------------------------------------------------------------------------- 1 | # This file may be used for custom Application configurations. 2 | # It will be loaded before other config files. 3 | # 4 | # Read more on configuration: 5 | # https://luckyframework.org/guides/getting-started/configuration#configuring-your-own-code 6 | 7 | # Use this code as an example: 8 | # 9 | # ``` 10 | # module Application 11 | # Habitat.create do 12 | # setting support_email : String 13 | # setting lock_with_basic_auth : Bool 14 | # end 15 | # end 16 | # 17 | # Application.configure do |settings| 18 | # settings.support_email = "support@myapp.io" 19 | # settings.lock_with_basic_auth = LuckyEnv.staging? 20 | # end 21 | # 22 | # # In your application, call 23 | # # `Application.settings.support_email` anywhere you need it. 24 | # ``` 25 | -------------------------------------------------------------------------------- /fixtures/src_template__api_only/expected/config/colors.cr: -------------------------------------------------------------------------------- 1 | # This enables the color output when in development or test 2 | # Check out the Colorize docs for more information 3 | # https://crystal-lang.org/api/Colorize.html 4 | Colorize.enabled = LuckyEnv.development? || LuckyEnv.test? 5 | -------------------------------------------------------------------------------- /fixtures/src_template__api_only/expected/config/cookies.cr: -------------------------------------------------------------------------------- 1 | require "./server" 2 | 3 | Lucky::Session.configure do |settings| 4 | settings.key = "_test_project_session" 5 | end 6 | 7 | Lucky::CookieJar.configure do |settings| 8 | settings.on_set = ->(cookie : HTTP::Cookie) { 9 | # If ForceSSLHandler is enabled, only send cookies over HTTPS 10 | cookie.secure(Lucky::ForceSSLHandler.settings.enabled) 11 | 12 | # By default, don't allow reading cookies with JavaScript 13 | cookie.http_only(true) 14 | 15 | # Restrict cookies to a first-party or same-site context 16 | cookie.samesite(:lax) 17 | 18 | # Set all cookies to the root path by default 19 | cookie.path("/") 20 | 21 | # You can set other defaults for cookies here. For example: 22 | # 23 | # cookie.expires(1.year.from_now).domain("mydomain.com") 24 | } 25 | end 26 | -------------------------------------------------------------------------------- /fixtures/src_template__api_only/expected/config/env.cr: -------------------------------------------------------------------------------- 1 | # Environments are managed using `LuckyEnv`. By default, development, production 2 | # and test are supported. See 3 | # https://luckyframework.org/guides/getting-started/configuration for details. 4 | # 5 | # The default environment is development unless the environment variable 6 | # LUCKY_ENV is set. 7 | # 8 | # Example: 9 | # ``` 10 | # LuckyEnv.environment # => "development" 11 | # LuckyEnv.development? # => true 12 | # LuckyEnv.production? # => false 13 | # LuckyEnv.test? # => false 14 | # ``` 15 | # 16 | # New environments can be added using the `LuckyEnv.add_env` macro. 17 | # 18 | # Example: 19 | # ``` 20 | # LuckyEnv.add_env :staging 21 | # LuckyEnv.staging? # => false 22 | # ``` 23 | # 24 | # To determine whether or not a `LuckyTask` is currently running, you can use 25 | # the `LuckyEnv.task?` predicate. 26 | # 27 | # Example: 28 | # ``` 29 | # LuckyEnv.task? # => false 30 | # ``` 31 | 32 | # Add a staging environment. 33 | # LuckyEnv.add_env :staging 34 | -------------------------------------------------------------------------------- /fixtures/src_template__api_only/expected/config/error_handler.cr: -------------------------------------------------------------------------------- 1 | Lucky::ErrorHandler.configure do |settings| 2 | settings.show_debug_output = !LuckyEnv.production? 3 | end 4 | -------------------------------------------------------------------------------- /fixtures/src_template__api_only/expected/config/route_helper.cr: -------------------------------------------------------------------------------- 1 | # This is used when generating URLs for your application 2 | Lucky::RouteHelper.configure do |settings| 3 | if LuckyEnv.production? 4 | # Example: https://my_app.com 5 | settings.base_uri = ENV.fetch("APP_DOMAIN") 6 | else 7 | # Set domain to the default host/port in development/test 8 | settings.base_uri = "http://localhost:#{Lucky::ServerSettings.port}" 9 | end 10 | end 11 | -------------------------------------------------------------------------------- /fixtures/src_template__api_only/expected/config/watch.yml: -------------------------------------------------------------------------------- 1 | host: 127.0.0.1 2 | port: 3000 3 | reload_port: 3001 4 | -------------------------------------------------------------------------------- /fixtures/src_template__api_only/expected/db/migrations/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luckyframework/lucky_cli/ccaa571765582086d437efe0da4f439a19929d64/fixtures/src_template__api_only/expected/db/migrations/.keep -------------------------------------------------------------------------------- /fixtures/src_template__api_only/expected/docker/development.dockerfile: -------------------------------------------------------------------------------- 1 | FROM crystallang/crystal:1.16.1 2 | 3 | # Install utilities required to make this Dockerfile run 4 | RUN apt-get update && \ 5 | apt-get install -y wget 6 | 7 | # Apt installs: 8 | # - Postgres cli tools are required for lucky-cli. 9 | # - tmux is required for the Overmind process manager. 10 | RUN apt-get update && \ 11 | apt-get install -y postgresql-client tmux && \ 12 | rm -rf /var/lib/apt/lists/* 13 | 14 | # Install lucky cli 15 | WORKDIR /lucky/cli 16 | RUN git clone https://github.com/luckyframework/lucky_cli . && \ 17 | git checkout v1.3.0 && \ 18 | shards build --without-development && \ 19 | cp bin/lucky /usr/bin 20 | 21 | WORKDIR /app 22 | ENV DATABASE_URL=postgres://postgres:postgres@host.docker.internal:5432/postgres 23 | EXPOSE 3000 24 | EXPOSE 3001 25 | 26 | -------------------------------------------------------------------------------- /fixtures/src_template__api_only/expected/script/helpers/function_helpers.cr: -------------------------------------------------------------------------------- 1 | require "colorize" 2 | 3 | # These are helper methods provided to help keep your code 4 | # clean. Add new methods, or alter these as needed. 5 | 6 | def notice(message : String) : Nil 7 | puts "\n▸ #{message}" 8 | end 9 | 10 | def print_done : Nil 11 | puts "✔ Done" 12 | end 13 | 14 | def print_error(message : String) : Nil 15 | puts "There is a problem with your system setup:\n".colorize.red.bold 16 | puts "#{message}\n".colorize.red.bold 17 | Process.exit(1) 18 | end 19 | 20 | def command_not_found(command : String) : Bool 21 | Process.find_executable(command).nil? 22 | end 23 | 24 | def command_not_running(command : String, *args) : Bool 25 | output = IO::Memory.new 26 | code = Process.run(command, args, output: output).exit_code 27 | code > 0 28 | end 29 | 30 | def run_command(command : String, *args) : Nil 31 | Process.run(command, args, output: STDOUT, error: STDERR, input: STDIN) 32 | end 33 | -------------------------------------------------------------------------------- /fixtures/src_template__api_only/expected/script/setup.cr: -------------------------------------------------------------------------------- 1 | require "./helpers/*" 2 | 3 | notice "Running System Check" 4 | 5 | require "./system_check" 6 | 7 | print_done 8 | 9 | 10 | notice "Installing shards" 11 | run_command "shards", "install" 12 | 13 | if !File.exists?(".env") 14 | notice "No .env found. Creating one." 15 | File.touch ".env" 16 | print_done 17 | end 18 | 19 | notice "Setting up the database" 20 | 21 | run_command "lucky", "db.setup" 22 | 23 | notice "Seeding the database with required and sample records" 24 | run_command "lucky", "db.seed.required_data" 25 | run_command "lucky", "db.seed.sample_data" 26 | 27 | print_done 28 | notice "Run 'lucky dev' to start the app" 29 | -------------------------------------------------------------------------------- /fixtures/src_template__api_only/expected/script/system_check.cr: -------------------------------------------------------------------------------- 1 | require "./helpers/*" 2 | 3 | # Use this script to check the system for required tools and process that your app needs. 4 | # A few helper functions are provided to keep the code simple. See the 5 | # script/helpers/function_helpers.cr file for more examples. 6 | # 7 | # A few examples you might use here: 8 | # * 'lucky db.verify_connection' to test postgres can be connected 9 | # * Checking that elasticsearch, redis, or postgres is installed and/or booted 10 | # * Note: Booting additional processes for things like mail, background jobs, etc... 11 | # should go in your Procfile.dev. 12 | 13 | 14 | # CUSTOM PRE-BOOT CHECKS 15 | # example: 16 | # if command_not_running "redis-cli", "ping" 17 | # print_error "Redis is not running." 18 | # end 19 | -------------------------------------------------------------------------------- /fixtures/src_template__api_only/expected/spec/setup/clean_database.cr: -------------------------------------------------------------------------------- 1 | Spec.before_each do 2 | AppDatabase.truncate 3 | end 4 | -------------------------------------------------------------------------------- /fixtures/src_template__api_only/expected/spec/setup/reset_emails.cr: -------------------------------------------------------------------------------- 1 | Spec.before_each do 2 | Carbon::DevAdapter.reset 3 | end 4 | -------------------------------------------------------------------------------- /fixtures/src_template__api_only/expected/spec/setup/setup_database.cr: -------------------------------------------------------------------------------- 1 | Db::Create.new(quiet: true).call 2 | Db::Migrate.new(quiet: true).call 3 | -------------------------------------------------------------------------------- /fixtures/src_template__api_only/expected/spec/setup/start_app_server.cr: -------------------------------------------------------------------------------- 1 | app_server = AppServer.new 2 | 3 | spawn do 4 | app_server.listen 5 | end 6 | 7 | Spec.after_suite do 8 | app_server.close 9 | end 10 | -------------------------------------------------------------------------------- /fixtures/src_template__api_only/expected/spec/spec_helper.cr: -------------------------------------------------------------------------------- 1 | ENV["LUCKY_ENV"] = "test" 2 | ENV["DEV_PORT"] = "5001" 3 | require "spec" 4 | require "../src/app" 5 | require "./support/**" 6 | require "../db/migrations/**" 7 | 8 | # Add/modify files in spec/setup to start/configure programs or run hooks 9 | # 10 | # By default there are scripts for setting up and cleaning the database, 11 | # configuring LuckyFlow, starting the app server, etc. 12 | require "./setup/**" 13 | 14 | include Carbon::Expectations 15 | include Lucky::RequestExpectations 16 | 17 | Avram::Migrator::Runner.new.ensure_migrated! 18 | Avram::SchemaEnforcer.ensure_correct_column_mappings! 19 | Habitat.raise_if_missing_settings! 20 | -------------------------------------------------------------------------------- /fixtures/src_template__api_only/expected/spec/support/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luckyframework/lucky_cli/ccaa571765582086d437efe0da4f439a19929d64/fixtures/src_template__api_only/expected/spec/support/.keep -------------------------------------------------------------------------------- /fixtures/src_template__api_only/expected/spec/support/api_client.cr: -------------------------------------------------------------------------------- 1 | class ApiClient < Lucky::BaseHTTPClient 2 | app AppServer.new 3 | 4 | def initialize 5 | super 6 | headers("Content-Type": "application/json") 7 | end 8 | end 9 | -------------------------------------------------------------------------------- /fixtures/src_template__api_only/expected/spec/support/factories/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luckyframework/lucky_cli/ccaa571765582086d437efe0da4f439a19929d64/fixtures/src_template__api_only/expected/spec/support/factories/.keep -------------------------------------------------------------------------------- /fixtures/src_template__api_only/expected/src/actions/api_action.cr: -------------------------------------------------------------------------------- 1 | # Include modules and add methods that are for all API requests 2 | abstract class ApiAction < Lucky::Action 3 | # APIs typically do not need to send cookie/session data. 4 | # Remove this line if you want to send cookies in the response header. 5 | disable_cookies 6 | accepted_formats [:json] 7 | 8 | # By default all actions are required to use underscores to separate words. 9 | # Add 'include Lucky::SkipRouteStyleCheck' to your actions if you wish to ignore this check for specific routes. 10 | include Lucky::EnforceUnderscoredRoute 11 | end 12 | -------------------------------------------------------------------------------- /fixtures/src_template__api_only/expected/src/actions/home/index.cr: -------------------------------------------------------------------------------- 1 | class Home::Index < ApiAction 2 | get "/" do 3 | json({hello: "Hello World from Home::Index"}) 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /fixtures/src_template__api_only/expected/src/actions/mixins/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luckyframework/lucky_cli/ccaa571765582086d437efe0da4f439a19929d64/fixtures/src_template__api_only/expected/src/actions/mixins/.keep -------------------------------------------------------------------------------- /fixtures/src_template__api_only/expected/src/app.cr: -------------------------------------------------------------------------------- 1 | require "./shards" 2 | 3 | require "../config/server" 4 | require "./app_database" 5 | require "../config/**" 6 | require "./models/base_model" 7 | require "./models/mixins/**" 8 | require "./models/**" 9 | require "./queries/mixins/**" 10 | require "./queries/**" 11 | require "./operations/mixins/**" 12 | require "./operations/**" 13 | require "./serializers/base_serializer" 14 | require "./serializers/**" 15 | require "./emails/base_email" 16 | require "./emails/**" 17 | require "./actions/mixins/**" 18 | require "./actions/**" 19 | require "../db/migrations/**" 20 | require "./app_server" 21 | -------------------------------------------------------------------------------- /fixtures/src_template__api_only/expected/src/app_database.cr: -------------------------------------------------------------------------------- 1 | class AppDatabase < Avram::Database 2 | end 3 | -------------------------------------------------------------------------------- /fixtures/src_template__api_only/expected/src/emails/base_email.cr: -------------------------------------------------------------------------------- 1 | # Learn about sending emails 2 | # https://luckyframework.org/guides/emails/sending-emails-with-carbon 3 | abstract class BaseEmail < Carbon::Email 4 | # You can add defaults using the 'inherited' hook 5 | # 6 | # Example: 7 | # 8 | # macro inherited 9 | # from default_from 10 | # end 11 | # 12 | # def default_from 13 | # Carbon::Address.new("support@app.com") 14 | # end 15 | end 16 | -------------------------------------------------------------------------------- /fixtures/src_template__api_only/expected/src/models/base_model.cr: -------------------------------------------------------------------------------- 1 | abstract class BaseModel < Avram::Model 2 | def self.database : Avram::Database.class 3 | AppDatabase 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /fixtures/src_template__api_only/expected/src/models/mixins/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luckyframework/lucky_cli/ccaa571765582086d437efe0da4f439a19929d64/fixtures/src_template__api_only/expected/src/models/mixins/.keep -------------------------------------------------------------------------------- /fixtures/src_template__api_only/expected/src/operations/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luckyframework/lucky_cli/ccaa571765582086d437efe0da4f439a19929d64/fixtures/src_template__api_only/expected/src/operations/.keep -------------------------------------------------------------------------------- /fixtures/src_template__api_only/expected/src/operations/mixins/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luckyframework/lucky_cli/ccaa571765582086d437efe0da4f439a19929d64/fixtures/src_template__api_only/expected/src/operations/mixins/.keep -------------------------------------------------------------------------------- /fixtures/src_template__api_only/expected/src/queries/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luckyframework/lucky_cli/ccaa571765582086d437efe0da4f439a19929d64/fixtures/src_template__api_only/expected/src/queries/.keep -------------------------------------------------------------------------------- /fixtures/src_template__api_only/expected/src/queries/mixins/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luckyframework/lucky_cli/ccaa571765582086d437efe0da4f439a19929d64/fixtures/src_template__api_only/expected/src/queries/mixins/.keep -------------------------------------------------------------------------------- /fixtures/src_template__api_only/expected/src/serializers/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luckyframework/lucky_cli/ccaa571765582086d437efe0da4f439a19929d64/fixtures/src_template__api_only/expected/src/serializers/.keep -------------------------------------------------------------------------------- /fixtures/src_template__api_only/expected/src/serializers/base_serializer.cr: -------------------------------------------------------------------------------- 1 | abstract class BaseSerializer 2 | include Lucky::Serializable 3 | 4 | def self.for_collection(collection : Enumerable, *args, **named_args) : Array(self) 5 | collection.map do |object| 6 | new(object, *args, **named_args) 7 | end 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /fixtures/src_template__api_only/expected/src/serializers/error_serializer.cr: -------------------------------------------------------------------------------- 1 | # This is the default error serializer generated by Lucky. 2 | # Feel free to customize it in any way you like. 3 | class ErrorSerializer < BaseSerializer 4 | def initialize( 5 | @message : String, 6 | @details : String? = nil, 7 | @param : String? = nil, # so you can track which param (if any) caused the problem 8 | ) 9 | end 10 | 11 | def render 12 | {message: @message, param: @param, details: @details} 13 | end 14 | end 15 | -------------------------------------------------------------------------------- /fixtures/src_template__api_only/expected/src/shards.cr: -------------------------------------------------------------------------------- 1 | # Load .env file before any other config or app code 2 | require "lucky_env" 3 | LuckyEnv.load?(".env") 4 | 5 | # Require your shards here 6 | require "lucky" 7 | require "avram/lucky" 8 | require "carbon" 9 | -------------------------------------------------------------------------------- /fixtures/src_template__api_only/expected/src/start_server.cr: -------------------------------------------------------------------------------- 1 | require "./app" 2 | 3 | Habitat.raise_if_missing_settings! 4 | 5 | if LuckyEnv.development? 6 | Avram::Migrator::Runner.new.ensure_migrated! 7 | Avram::SchemaEnforcer.ensure_correct_column_mappings! 8 | end 9 | 10 | app_server = AppServer.new 11 | puts "Listening on http://#{app_server.host}:#{app_server.port}" 12 | 13 | Signal::INT.trap do 14 | app_server.close 15 | end 16 | 17 | app_server.listen 18 | -------------------------------------------------------------------------------- /fixtures/src_template__api_only/expected/src/test_project.cr: -------------------------------------------------------------------------------- 1 | # Typically you will not use or modify this file. 'shards build' and some 2 | # other crystal tools will sometimes use this. 3 | # 4 | # When this file is compiled/run it will require and run 'start_server', 5 | # which as its name implies will start the server for you app. 6 | require "./start_server" 7 | -------------------------------------------------------------------------------- /fixtures/src_template__api_only/expected/tasks.cr: -------------------------------------------------------------------------------- 1 | # This file loads your app and all your tasks when running 'lucky' 2 | # 3 | # Run 'lucky --help' to see all available tasks. 4 | # 5 | # Learn to create your own tasks: 6 | # https://luckyframework.org/guides/command-line-tasks/custom-tasks 7 | 8 | # See `LuckyEnv#task?` 9 | ENV["LUCKY_TASK"] = "true" 10 | 11 | # Load Lucky and the app (actions, models, etc.) 12 | require "./src/app" 13 | require "lucky_task" 14 | 15 | # You can add your own tasks here in the ./tasks folder 16 | require "./tasks/**" 17 | 18 | # Load migrations 19 | require "./db/migrations/**" 20 | 21 | # Load Lucky tasks (dev, routes, etc.) 22 | require "lucky/tasks/**" 23 | require "avram/lucky/tasks" 24 | 25 | LuckyTask::Runner.run 26 | -------------------------------------------------------------------------------- /fixtures/src_template__api_only/expected/tasks/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luckyframework/lucky_cli/ccaa571765582086d437efe0da4f439a19929d64/fixtures/src_template__api_only/expected/tasks/.keep -------------------------------------------------------------------------------- /fixtures/src_template__generate_auth/expected/.crystal-version: -------------------------------------------------------------------------------- 1 | 1.16.1 2 | -------------------------------------------------------------------------------- /fixtures/src_template__generate_auth/expected/.env: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luckyframework/lucky_cli/ccaa571765582086d437efe0da4f439a19929d64/fixtures/src_template__generate_auth/expected/.env -------------------------------------------------------------------------------- /fixtures/src_template__generate_auth/expected/Procfile: -------------------------------------------------------------------------------- 1 | web: bin/app 2 | release: lucky db.migrate 3 | -------------------------------------------------------------------------------- /fixtures/src_template__generate_auth/expected/Procfile.dev: -------------------------------------------------------------------------------- 1 | system_check: crystal script/system_check.cr 2 | web: lucky watch --reload-browser 3 | assets: yarn watch 4 | -------------------------------------------------------------------------------- /fixtures/src_template__generate_auth/expected/config/application.cr: -------------------------------------------------------------------------------- 1 | # This file may be used for custom Application configurations. 2 | # It will be loaded before other config files. 3 | # 4 | # Read more on configuration: 5 | # https://luckyframework.org/guides/getting-started/configuration#configuring-your-own-code 6 | 7 | # Use this code as an example: 8 | # 9 | # ``` 10 | # module Application 11 | # Habitat.create do 12 | # setting support_email : String 13 | # setting lock_with_basic_auth : Bool 14 | # end 15 | # end 16 | # 17 | # Application.configure do |settings| 18 | # settings.support_email = "support@myapp.io" 19 | # settings.lock_with_basic_auth = LuckyEnv.staging? 20 | # end 21 | # 22 | # # In your application, call 23 | # # `Application.settings.support_email` anywhere you need it. 24 | # ``` 25 | -------------------------------------------------------------------------------- /fixtures/src_template__generate_auth/expected/config/colors.cr: -------------------------------------------------------------------------------- 1 | # This enables the color output when in development or test 2 | # Check out the Colorize docs for more information 3 | # https://crystal-lang.org/api/Colorize.html 4 | Colorize.enabled = LuckyEnv.development? || LuckyEnv.test? 5 | -------------------------------------------------------------------------------- /fixtures/src_template__generate_auth/expected/config/cookies.cr: -------------------------------------------------------------------------------- 1 | require "./server" 2 | 3 | Lucky::Session.configure do |settings| 4 | settings.key = "_test_project_session" 5 | end 6 | 7 | Lucky::CookieJar.configure do |settings| 8 | settings.on_set = ->(cookie : HTTP::Cookie) { 9 | # If ForceSSLHandler is enabled, only send cookies over HTTPS 10 | cookie.secure(Lucky::ForceSSLHandler.settings.enabled) 11 | 12 | # By default, don't allow reading cookies with JavaScript 13 | cookie.http_only(true) 14 | 15 | # Restrict cookies to a first-party or same-site context 16 | cookie.samesite(:lax) 17 | 18 | # Set all cookies to the root path by default 19 | cookie.path("/") 20 | 21 | # You can set other defaults for cookies here. For example: 22 | # 23 | # cookie.expires(1.year.from_now).domain("mydomain.com") 24 | } 25 | end 26 | -------------------------------------------------------------------------------- /fixtures/src_template__generate_auth/expected/config/error_handler.cr: -------------------------------------------------------------------------------- 1 | Lucky::ErrorHandler.configure do |settings| 2 | settings.show_debug_output = !LuckyEnv.production? 3 | end 4 | -------------------------------------------------------------------------------- /fixtures/src_template__generate_auth/expected/config/route_helper.cr: -------------------------------------------------------------------------------- 1 | # This is used when generating URLs for your application 2 | Lucky::RouteHelper.configure do |settings| 3 | if LuckyEnv.production? 4 | # Example: https://my_app.com 5 | settings.base_uri = ENV.fetch("APP_DOMAIN") 6 | else 7 | # Set domain to the default host/port in development/test 8 | settings.base_uri = "http://localhost:#{Lucky::ServerSettings.port}" 9 | end 10 | end 11 | -------------------------------------------------------------------------------- /fixtures/src_template__generate_auth/expected/config/watch.yml: -------------------------------------------------------------------------------- 1 | host: 127.0.0.1 2 | port: 3000 3 | reload_port: 3001 4 | -------------------------------------------------------------------------------- /fixtures/src_template__generate_auth/expected/db/migrations/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luckyframework/lucky_cli/ccaa571765582086d437efe0da4f439a19929d64/fixtures/src_template__generate_auth/expected/db/migrations/.keep -------------------------------------------------------------------------------- /fixtures/src_template__generate_auth/expected/script/helpers/function_helpers.cr: -------------------------------------------------------------------------------- 1 | require "colorize" 2 | 3 | # These are helper methods provided to help keep your code 4 | # clean. Add new methods, or alter these as needed. 5 | 6 | def notice(message : String) : Nil 7 | puts "\n▸ #{message}" 8 | end 9 | 10 | def print_done : Nil 11 | puts "✔ Done" 12 | end 13 | 14 | def print_error(message : String) : Nil 15 | puts "There is a problem with your system setup:\n".colorize.red.bold 16 | puts "#{message}\n".colorize.red.bold 17 | Process.exit(1) 18 | end 19 | 20 | def command_not_found(command : String) : Bool 21 | Process.find_executable(command).nil? 22 | end 23 | 24 | def command_not_running(command : String, *args) : Bool 25 | output = IO::Memory.new 26 | code = Process.run(command, args, output: output).exit_code 27 | code > 0 28 | end 29 | 30 | def run_command(command : String, *args) : Nil 31 | Process.run(command, args, output: STDOUT, error: STDERR, input: STDIN) 32 | end 33 | -------------------------------------------------------------------------------- /fixtures/src_template__generate_auth/expected/script/setup.cr: -------------------------------------------------------------------------------- 1 | require "./helpers/*" 2 | 3 | notice "Running System Check" 4 | 5 | require "./system_check" 6 | 7 | print_done 8 | 9 | notice "Installing node dependencies" 10 | run_command "yarn", "install", "--no-progress" 11 | 12 | notice "Compiling assets" 13 | run_command "yarn", "dev" 14 | 15 | print_done 16 | 17 | notice "Installing shards" 18 | run_command "shards", "install" 19 | 20 | if !File.exists?(".env") 21 | notice "No .env found. Creating one." 22 | File.touch ".env" 23 | print_done 24 | end 25 | 26 | notice "Setting up the database" 27 | 28 | run_command "lucky", "db.setup" 29 | 30 | notice "Seeding the database with required and sample records" 31 | run_command "lucky", "db.seed.required_data" 32 | run_command "lucky", "db.seed.sample_data" 33 | 34 | print_done 35 | notice "Run 'lucky dev' to start the app" 36 | -------------------------------------------------------------------------------- /fixtures/src_template__generate_auth/expected/script/system_check.cr: -------------------------------------------------------------------------------- 1 | require "./helpers/*" 2 | 3 | # Use this script to check the system for required tools and process that your app needs. 4 | # A few helper functions are provided to keep the code simple. See the 5 | # script/helpers/function_helpers.cr file for more examples. 6 | # 7 | # A few examples you might use here: 8 | # * 'lucky db.verify_connection' to test postgres can be connected 9 | # * Checking that elasticsearch, redis, or postgres is installed and/or booted 10 | # * Note: Booting additional processes for things like mail, background jobs, etc... 11 | # should go in your Procfile.dev. 12 | 13 | if command_not_found "yarn" 14 | print_error "Yarn is not installed\n See https://yarnpkg.com/lang/en/docs/install/ for install instructions." 15 | end 16 | 17 | # CUSTOM PRE-BOOT CHECKS 18 | # example: 19 | # if command_not_running "redis-cli", "ping" 20 | # print_error "Redis is not running." 21 | # end 22 | -------------------------------------------------------------------------------- /fixtures/src_template__generate_auth/expected/spec/setup/clean_database.cr: -------------------------------------------------------------------------------- 1 | Spec.before_each do 2 | AppDatabase.truncate 3 | end 4 | -------------------------------------------------------------------------------- /fixtures/src_template__generate_auth/expected/spec/setup/reset_emails.cr: -------------------------------------------------------------------------------- 1 | Spec.before_each do 2 | Carbon::DevAdapter.reset 3 | end 4 | -------------------------------------------------------------------------------- /fixtures/src_template__generate_auth/expected/spec/setup/setup_database.cr: -------------------------------------------------------------------------------- 1 | Db::Create.new(quiet: true).call 2 | Db::Migrate.new(quiet: true).call 3 | -------------------------------------------------------------------------------- /fixtures/src_template__generate_auth/expected/spec/setup/start_app_server.cr: -------------------------------------------------------------------------------- 1 | app_server = AppServer.new 2 | 3 | spawn do 4 | app_server.listen 5 | end 6 | 7 | Spec.after_suite do 8 | LuckyFlow.shutdown 9 | app_server.close 10 | end 11 | -------------------------------------------------------------------------------- /fixtures/src_template__generate_auth/expected/spec/spec_helper.cr: -------------------------------------------------------------------------------- 1 | ENV["LUCKY_ENV"] = "test" 2 | ENV["DEV_PORT"] = "5001" 3 | require "spec" 4 | require "lucky_flow" 5 | require "lucky_flow/ext/lucky" 6 | require "lucky_flow/ext/avram" 7 | 8 | require "lucky_flow/ext/authentic" 9 | require "../src/app" 10 | require "./support/flows/base_flow" 11 | require "./support/**" 12 | require "../db/migrations/**" 13 | 14 | # Add/modify files in spec/setup to start/configure programs or run hooks 15 | # 16 | # By default there are scripts for setting up and cleaning the database, 17 | # configuring LuckyFlow, starting the app server, etc. 18 | require "./setup/**" 19 | 20 | include Carbon::Expectations 21 | include Lucky::RequestExpectations 22 | include LuckyFlow::Expectations 23 | 24 | Avram::Migrator::Runner.new.ensure_migrated! 25 | Avram::SchemaEnforcer.ensure_correct_column_mappings! 26 | Habitat.raise_if_missing_settings! 27 | -------------------------------------------------------------------------------- /fixtures/src_template__generate_auth/expected/spec/support/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luckyframework/lucky_cli/ccaa571765582086d437efe0da4f439a19929d64/fixtures/src_template__generate_auth/expected/spec/support/.keep -------------------------------------------------------------------------------- /fixtures/src_template__generate_auth/expected/spec/support/api_client.cr: -------------------------------------------------------------------------------- 1 | class ApiClient < Lucky::BaseHTTPClient 2 | app AppServer.new 3 | 4 | def initialize 5 | super 6 | headers("Content-Type": "application/json") 7 | end 8 | 9 | def self.auth(user : User) 10 | new.headers("Authorization": UserToken.generate(user)) 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /fixtures/src_template__generate_auth/expected/spec/support/factories/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luckyframework/lucky_cli/ccaa571765582086d437efe0da4f439a19929d64/fixtures/src_template__generate_auth/expected/spec/support/factories/.keep -------------------------------------------------------------------------------- /fixtures/src_template__generate_auth/expected/src/actions/api_action.cr: -------------------------------------------------------------------------------- 1 | # Include modules and add methods that are for all API requests 2 | abstract class ApiAction < Lucky::Action 3 | # APIs typically do not need to send cookie/session data. 4 | # Remove this line if you want to send cookies in the response header. 5 | disable_cookies 6 | accepted_formats [:json] 7 | 8 | include Api::Auth::Helpers 9 | 10 | # By default all actions require sign in. 11 | # Add 'include Api::Auth::SkipRequireAuthToken' to your actions to allow all requests. 12 | include Api::Auth::RequireAuthToken 13 | 14 | # By default all actions are required to use underscores to separate words. 15 | # Add 'include Lucky::SkipRouteStyleCheck' to your actions if you wish to ignore this check for specific routes. 16 | include Lucky::EnforceUnderscoredRoute 17 | end 18 | -------------------------------------------------------------------------------- /fixtures/src_template__generate_auth/expected/src/actions/home/index.cr: -------------------------------------------------------------------------------- 1 | class Home::Index < BrowserAction 2 | include Auth::AllowGuests 3 | 4 | get "/" do 5 | if current_user? 6 | redirect Me::Show 7 | else 8 | # When you're ready change this line to: 9 | # 10 | # redirect SignIns::New 11 | # 12 | # Or maybe show signed out users a marketing page: 13 | # 14 | # html Marketing::IndexPage 15 | html Lucky::WelcomePage 16 | end 17 | end 18 | end 19 | -------------------------------------------------------------------------------- /fixtures/src_template__generate_auth/expected/src/actions/mixins/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luckyframework/lucky_cli/ccaa571765582086d437efe0da4f439a19929d64/fixtures/src_template__generate_auth/expected/src/actions/mixins/.keep -------------------------------------------------------------------------------- /fixtures/src_template__generate_auth/expected/src/app.cr: -------------------------------------------------------------------------------- 1 | require "./shards" 2 | 3 | # Load the asset manifest 4 | Lucky::AssetHelpers.load_manifest "public/mix-manifest.json" 5 | 6 | require "../config/server" 7 | require "./app_database" 8 | require "../config/**" 9 | require "./models/base_model" 10 | require "./models/mixins/**" 11 | require "./models/**" 12 | require "./queries/mixins/**" 13 | require "./queries/**" 14 | require "./operations/mixins/**" 15 | require "./operations/**" 16 | require "./serializers/base_serializer" 17 | require "./serializers/**" 18 | require "./emails/base_email" 19 | require "./emails/**" 20 | require "./actions/mixins/**" 21 | require "./actions/**" 22 | require "./components/base_component" 23 | require "./components/**" 24 | require "./pages/**" 25 | require "../db/migrations/**" 26 | require "./app_server" 27 | -------------------------------------------------------------------------------- /fixtures/src_template__generate_auth/expected/src/app_database.cr: -------------------------------------------------------------------------------- 1 | class AppDatabase < Avram::Database 2 | end 3 | -------------------------------------------------------------------------------- /fixtures/src_template__generate_auth/expected/src/app_server.cr: -------------------------------------------------------------------------------- 1 | class AppServer < Lucky::BaseAppServer 2 | # Learn about middleware with HTTP::Handlers: 3 | # https://luckyframework.org/guides/http-and-routing/http-handlers 4 | def middleware : Array(HTTP::Handler) 5 | [ 6 | Lucky::RequestIdHandler.new, 7 | Lucky::ForceSSLHandler.new, 8 | Lucky::HttpMethodOverrideHandler.new, 9 | Lucky::LogHandler.new, 10 | Lucky::ErrorHandler.new(action: Errors::Show), 11 | Lucky::RemoteIpHandler.new, 12 | Lucky::RouteHandler.new, 13 | Lucky::StaticCompressionHandler.new("./public", file_ext: "gz", content_encoding: "gzip"), 14 | Lucky::StaticFileHandler.new("./public", fallthrough: false, directory_listing: false), 15 | Lucky::RouteNotFoundHandler.new, 16 | ] of HTTP::Handler 17 | end 18 | 19 | def protocol 20 | "http" 21 | end 22 | 23 | def listen 24 | server.listen(host, port, reuse_port: false) 25 | end 26 | end 27 | -------------------------------------------------------------------------------- /fixtures/src_template__generate_auth/expected/src/emails/base_email.cr: -------------------------------------------------------------------------------- 1 | # Learn about sending emails 2 | # https://luckyframework.org/guides/emails/sending-emails-with-carbon 3 | abstract class BaseEmail < Carbon::Email 4 | # You can add defaults using the 'inherited' hook 5 | # 6 | # Example: 7 | # 8 | # macro inherited 9 | # from default_from 10 | # end 11 | # 12 | # def default_from 13 | # Carbon::Address.new("support@app.com") 14 | # end 15 | end 16 | -------------------------------------------------------------------------------- /fixtures/src_template__generate_auth/expected/src/models/base_model.cr: -------------------------------------------------------------------------------- 1 | abstract class BaseModel < Avram::Model 2 | def self.database : Avram::Database.class 3 | AppDatabase 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /fixtures/src_template__generate_auth/expected/src/models/mixins/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luckyframework/lucky_cli/ccaa571765582086d437efe0da4f439a19929d64/fixtures/src_template__generate_auth/expected/src/models/mixins/.keep -------------------------------------------------------------------------------- /fixtures/src_template__generate_auth/expected/src/operations/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luckyframework/lucky_cli/ccaa571765582086d437efe0da4f439a19929d64/fixtures/src_template__generate_auth/expected/src/operations/.keep -------------------------------------------------------------------------------- /fixtures/src_template__generate_auth/expected/src/operations/mixins/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luckyframework/lucky_cli/ccaa571765582086d437efe0da4f439a19929d64/fixtures/src_template__generate_auth/expected/src/operations/mixins/.keep -------------------------------------------------------------------------------- /fixtures/src_template__generate_auth/expected/src/queries/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luckyframework/lucky_cli/ccaa571765582086d437efe0da4f439a19929d64/fixtures/src_template__generate_auth/expected/src/queries/.keep -------------------------------------------------------------------------------- /fixtures/src_template__generate_auth/expected/src/queries/mixins/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luckyframework/lucky_cli/ccaa571765582086d437efe0da4f439a19929d64/fixtures/src_template__generate_auth/expected/src/queries/mixins/.keep -------------------------------------------------------------------------------- /fixtures/src_template__generate_auth/expected/src/serializers/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luckyframework/lucky_cli/ccaa571765582086d437efe0da4f439a19929d64/fixtures/src_template__generate_auth/expected/src/serializers/.keep -------------------------------------------------------------------------------- /fixtures/src_template__generate_auth/expected/src/serializers/base_serializer.cr: -------------------------------------------------------------------------------- 1 | abstract class BaseSerializer 2 | include Lucky::Serializable 3 | 4 | def self.for_collection(collection : Enumerable, *args, **named_args) : Array(self) 5 | collection.map do |object| 6 | new(object, *args, **named_args) 7 | end 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /fixtures/src_template__generate_auth/expected/src/serializers/error_serializer.cr: -------------------------------------------------------------------------------- 1 | # This is the default error serializer generated by Lucky. 2 | # Feel free to customize it in any way you like. 3 | class ErrorSerializer < BaseSerializer 4 | def initialize( 5 | @message : String, 6 | @details : String? = nil, 7 | @param : String? = nil, # so you can track which param (if any) caused the problem 8 | ) 9 | end 10 | 11 | def render 12 | {message: @message, param: @param, details: @details} 13 | end 14 | end 15 | -------------------------------------------------------------------------------- /fixtures/src_template__generate_auth/expected/src/shards.cr: -------------------------------------------------------------------------------- 1 | # Load .env file before any other config or app code 2 | require "lucky_env" 3 | LuckyEnv.load?(".env") 4 | 5 | # Require your shards here 6 | require "lucky" 7 | require "avram/lucky" 8 | require "carbon" 9 | require "authentic" 10 | require "jwt" 11 | -------------------------------------------------------------------------------- /fixtures/src_template__generate_auth/expected/src/start_server.cr: -------------------------------------------------------------------------------- 1 | require "./app" 2 | 3 | Habitat.raise_if_missing_settings! 4 | 5 | if LuckyEnv.development? 6 | Avram::Migrator::Runner.new.ensure_migrated! 7 | Avram::SchemaEnforcer.ensure_correct_column_mappings! 8 | end 9 | 10 | app_server = AppServer.new 11 | 12 | Signal::INT.trap do 13 | app_server.close 14 | end 15 | 16 | app_server.listen 17 | -------------------------------------------------------------------------------- /fixtures/src_template__generate_auth/expected/src/test_project.cr: -------------------------------------------------------------------------------- 1 | # Typically you will not use or modify this file. 'shards build' and some 2 | # other crystal tools will sometimes use this. 3 | # 4 | # When this file is compiled/run it will require and run 'start_server', 5 | # which as its name implies will start the server for you app. 6 | require "./start_server" 7 | -------------------------------------------------------------------------------- /fixtures/src_template__generate_auth/expected/tasks.cr: -------------------------------------------------------------------------------- 1 | # This file loads your app and all your tasks when running 'lucky' 2 | # 3 | # Run 'lucky --help' to see all available tasks. 4 | # 5 | # Learn to create your own tasks: 6 | # https://luckyframework.org/guides/command-line-tasks/custom-tasks 7 | 8 | # See `LuckyEnv#task?` 9 | ENV["LUCKY_TASK"] = "true" 10 | 11 | # Load Lucky and the app (actions, models, etc.) 12 | require "./src/app" 13 | require "lucky_task" 14 | 15 | # You can add your own tasks here in the ./tasks folder 16 | require "./tasks/**" 17 | 18 | # Load migrations 19 | require "./db/migrations/**" 20 | 21 | # Load Lucky tasks (dev, routes, etc.) 22 | require "lucky/tasks/**" 23 | require "avram/lucky/tasks" 24 | 25 | LuckyTask::Runner.run 26 | -------------------------------------------------------------------------------- /fixtures/src_template__generate_auth/expected/tasks/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luckyframework/lucky_cli/ccaa571765582086d437efe0da4f439a19929d64/fixtures/src_template__generate_auth/expected/tasks/.keep -------------------------------------------------------------------------------- /fixtures/src_template__sec_tester/expected/.crystal-version: -------------------------------------------------------------------------------- 1 | 1.16.1 2 | -------------------------------------------------------------------------------- /fixtures/src_template__sec_tester/expected/.env: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luckyframework/lucky_cli/ccaa571765582086d437efe0da4f439a19929d64/fixtures/src_template__sec_tester/expected/.env -------------------------------------------------------------------------------- /fixtures/src_template__sec_tester/expected/Procfile: -------------------------------------------------------------------------------- 1 | web: bin/app 2 | release: lucky db.migrate 3 | -------------------------------------------------------------------------------- /fixtures/src_template__sec_tester/expected/Procfile.dev: -------------------------------------------------------------------------------- 1 | system_check: crystal script/system_check.cr 2 | web: lucky watch --reload-browser 3 | assets: yarn watch 4 | -------------------------------------------------------------------------------- /fixtures/src_template__sec_tester/expected/config/application.cr: -------------------------------------------------------------------------------- 1 | # This file may be used for custom Application configurations. 2 | # It will be loaded before other config files. 3 | # 4 | # Read more on configuration: 5 | # https://luckyframework.org/guides/getting-started/configuration#configuring-your-own-code 6 | 7 | # Use this code as an example: 8 | # 9 | # ``` 10 | # module Application 11 | # Habitat.create do 12 | # setting support_email : String 13 | # setting lock_with_basic_auth : Bool 14 | # end 15 | # end 16 | # 17 | # Application.configure do |settings| 18 | # settings.support_email = "support@myapp.io" 19 | # settings.lock_with_basic_auth = LuckyEnv.staging? 20 | # end 21 | # 22 | # # In your application, call 23 | # # `Application.settings.support_email` anywhere you need it. 24 | # ``` 25 | -------------------------------------------------------------------------------- /fixtures/src_template__sec_tester/expected/config/colors.cr: -------------------------------------------------------------------------------- 1 | # This enables the color output when in development or test 2 | # Check out the Colorize docs for more information 3 | # https://crystal-lang.org/api/Colorize.html 4 | Colorize.enabled = LuckyEnv.development? || LuckyEnv.test? 5 | -------------------------------------------------------------------------------- /fixtures/src_template__sec_tester/expected/config/cookies.cr: -------------------------------------------------------------------------------- 1 | require "./server" 2 | 3 | Lucky::Session.configure do |settings| 4 | settings.key = "_test_project_session" 5 | end 6 | 7 | Lucky::CookieJar.configure do |settings| 8 | settings.on_set = ->(cookie : HTTP::Cookie) { 9 | # If ForceSSLHandler is enabled, only send cookies over HTTPS 10 | cookie.secure(Lucky::ForceSSLHandler.settings.enabled) 11 | 12 | # By default, don't allow reading cookies with JavaScript 13 | cookie.http_only(true) 14 | 15 | # Restrict cookies to a first-party or same-site context 16 | cookie.samesite(:lax) 17 | 18 | # Set all cookies to the root path by default 19 | cookie.path("/") 20 | 21 | # You can set other defaults for cookies here. For example: 22 | # 23 | # cookie.expires(1.year.from_now).domain("mydomain.com") 24 | } 25 | end 26 | -------------------------------------------------------------------------------- /fixtures/src_template__sec_tester/expected/config/error_handler.cr: -------------------------------------------------------------------------------- 1 | Lucky::ErrorHandler.configure do |settings| 2 | settings.show_debug_output = !LuckyEnv.production? 3 | end 4 | -------------------------------------------------------------------------------- /fixtures/src_template__sec_tester/expected/config/route_helper.cr: -------------------------------------------------------------------------------- 1 | # This is used when generating URLs for your application 2 | Lucky::RouteHelper.configure do |settings| 3 | if LuckyEnv.production? 4 | # Example: https://my_app.com 5 | settings.base_uri = ENV.fetch("APP_DOMAIN") 6 | else 7 | # Set domain to the default host/port in development/test 8 | settings.base_uri = "http://localhost:#{Lucky::ServerSettings.port}" 9 | end 10 | end 11 | -------------------------------------------------------------------------------- /fixtures/src_template__sec_tester/expected/config/watch.yml: -------------------------------------------------------------------------------- 1 | host: 127.0.0.1 2 | port: 3000 3 | reload_port: 3001 4 | -------------------------------------------------------------------------------- /fixtures/src_template__sec_tester/expected/db/migrations/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luckyframework/lucky_cli/ccaa571765582086d437efe0da4f439a19929d64/fixtures/src_template__sec_tester/expected/db/migrations/.keep -------------------------------------------------------------------------------- /fixtures/src_template__sec_tester/expected/script/helpers/function_helpers.cr: -------------------------------------------------------------------------------- 1 | require "colorize" 2 | 3 | # These are helper methods provided to help keep your code 4 | # clean. Add new methods, or alter these as needed. 5 | 6 | def notice(message : String) : Nil 7 | puts "\n▸ #{message}" 8 | end 9 | 10 | def print_done : Nil 11 | puts "✔ Done" 12 | end 13 | 14 | def print_error(message : String) : Nil 15 | puts "There is a problem with your system setup:\n".colorize.red.bold 16 | puts "#{message}\n".colorize.red.bold 17 | Process.exit(1) 18 | end 19 | 20 | def command_not_found(command : String) : Bool 21 | Process.find_executable(command).nil? 22 | end 23 | 24 | def command_not_running(command : String, *args) : Bool 25 | output = IO::Memory.new 26 | code = Process.run(command, args, output: output).exit_code 27 | code > 0 28 | end 29 | 30 | def run_command(command : String, *args) : Nil 31 | Process.run(command, args, output: STDOUT, error: STDERR, input: STDIN) 32 | end 33 | -------------------------------------------------------------------------------- /fixtures/src_template__sec_tester/expected/script/setup.cr: -------------------------------------------------------------------------------- 1 | require "./helpers/*" 2 | 3 | notice "Running System Check" 4 | 5 | require "./system_check" 6 | 7 | print_done 8 | 9 | notice "Installing node dependencies" 10 | run_command "yarn", "install", "--no-progress" 11 | 12 | notice "Compiling assets" 13 | run_command "yarn", "dev" 14 | 15 | print_done 16 | 17 | notice "Installing shards" 18 | run_command "shards", "install" 19 | 20 | if !File.exists?(".env") 21 | notice "No .env found. Creating one." 22 | File.touch ".env" 23 | print_done 24 | end 25 | 26 | notice "Setting up the database" 27 | 28 | run_command "lucky", "db.setup" 29 | 30 | notice "Seeding the database with required and sample records" 31 | run_command "lucky", "db.seed.required_data" 32 | run_command "lucky", "db.seed.sample_data" 33 | 34 | print_done 35 | notice "Run 'lucky dev' to start the app" 36 | -------------------------------------------------------------------------------- /fixtures/src_template__sec_tester/expected/script/system_check.cr: -------------------------------------------------------------------------------- 1 | require "./helpers/*" 2 | 3 | # Use this script to check the system for required tools and process that your app needs. 4 | # A few helper functions are provided to keep the code simple. See the 5 | # script/helpers/function_helpers.cr file for more examples. 6 | # 7 | # A few examples you might use here: 8 | # * 'lucky db.verify_connection' to test postgres can be connected 9 | # * Checking that elasticsearch, redis, or postgres is installed and/or booted 10 | # * Note: Booting additional processes for things like mail, background jobs, etc... 11 | # should go in your Procfile.dev. 12 | 13 | if command_not_found "yarn" 14 | print_error "Yarn is not installed\n See https://yarnpkg.com/lang/en/docs/install/ for install instructions." 15 | end 16 | 17 | # CUSTOM PRE-BOOT CHECKS 18 | # example: 19 | # if command_not_running "redis-cli", "ping" 20 | # print_error "Redis is not running." 21 | # end 22 | -------------------------------------------------------------------------------- /fixtures/src_template__sec_tester/expected/spec/setup/clean_database.cr: -------------------------------------------------------------------------------- 1 | Spec.before_each do 2 | AppDatabase.truncate 3 | end 4 | -------------------------------------------------------------------------------- /fixtures/src_template__sec_tester/expected/spec/setup/reset_emails.cr: -------------------------------------------------------------------------------- 1 | Spec.before_each do 2 | Carbon::DevAdapter.reset 3 | end 4 | -------------------------------------------------------------------------------- /fixtures/src_template__sec_tester/expected/spec/setup/setup_database.cr: -------------------------------------------------------------------------------- 1 | Db::Create.new(quiet: true).call 2 | Db::Migrate.new(quiet: true).call 3 | -------------------------------------------------------------------------------- /fixtures/src_template__sec_tester/expected/spec/setup/start_app_server.cr: -------------------------------------------------------------------------------- 1 | app_server = AppServer.new 2 | 3 | spawn do 4 | app_server.listen 5 | end 6 | 7 | Spec.after_suite do 8 | LuckyFlow.shutdown 9 | app_server.close 10 | end 11 | -------------------------------------------------------------------------------- /fixtures/src_template__sec_tester/expected/spec/spec_helper.cr: -------------------------------------------------------------------------------- 1 | ENV["LUCKY_ENV"] = "test" 2 | ENV["DEV_PORT"] = "5001" 3 | require "spec" 4 | require "lucky_flow" 5 | require "lucky_flow/ext/lucky" 6 | require "lucky_flow/ext/avram" 7 | require "../src/app" 8 | require "./support/flows/base_flow" 9 | require "./support/**" 10 | require "../db/migrations/**" 11 | 12 | # Add/modify files in spec/setup to start/configure programs or run hooks 13 | # 14 | # By default there are scripts for setting up and cleaning the database, 15 | # configuring LuckyFlow, starting the app server, etc. 16 | require "./setup/**" 17 | 18 | include Carbon::Expectations 19 | include Lucky::RequestExpectations 20 | include LuckyFlow::Expectations 21 | 22 | Avram::Migrator::Runner.new.ensure_migrated! 23 | Avram::SchemaEnforcer.ensure_correct_column_mappings! 24 | Habitat.raise_if_missing_settings! 25 | -------------------------------------------------------------------------------- /fixtures/src_template__sec_tester/expected/spec/support/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luckyframework/lucky_cli/ccaa571765582086d437efe0da4f439a19929d64/fixtures/src_template__sec_tester/expected/spec/support/.keep -------------------------------------------------------------------------------- /fixtures/src_template__sec_tester/expected/spec/support/api_client.cr: -------------------------------------------------------------------------------- 1 | class ApiClient < Lucky::BaseHTTPClient 2 | app AppServer.new 3 | 4 | def initialize 5 | super 6 | headers("Content-Type": "application/json") 7 | end 8 | end 9 | -------------------------------------------------------------------------------- /fixtures/src_template__sec_tester/expected/spec/support/factories/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luckyframework/lucky_cli/ccaa571765582086d437efe0da4f439a19929d64/fixtures/src_template__sec_tester/expected/spec/support/factories/.keep -------------------------------------------------------------------------------- /fixtures/src_template__sec_tester/expected/src/actions/api_action.cr: -------------------------------------------------------------------------------- 1 | # Include modules and add methods that are for all API requests 2 | abstract class ApiAction < Lucky::Action 3 | # APIs typically do not need to send cookie/session data. 4 | # Remove this line if you want to send cookies in the response header. 5 | disable_cookies 6 | accepted_formats [:json] 7 | 8 | # By default all actions are required to use underscores to separate words. 9 | # Add 'include Lucky::SkipRouteStyleCheck' to your actions if you wish to ignore this check for specific routes. 10 | include Lucky::EnforceUnderscoredRoute 11 | end 12 | -------------------------------------------------------------------------------- /fixtures/src_template__sec_tester/expected/src/actions/home/index.cr: -------------------------------------------------------------------------------- 1 | class Home::Index < BrowserAction 2 | get "/" do 3 | html Lucky::WelcomePage 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /fixtures/src_template__sec_tester/expected/src/actions/mixins/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luckyframework/lucky_cli/ccaa571765582086d437efe0da4f439a19929d64/fixtures/src_template__sec_tester/expected/src/actions/mixins/.keep -------------------------------------------------------------------------------- /fixtures/src_template__sec_tester/expected/src/app.cr: -------------------------------------------------------------------------------- 1 | require "./shards" 2 | 3 | # Load the asset manifest 4 | Lucky::AssetHelpers.load_manifest "public/mix-manifest.json" 5 | 6 | require "../config/server" 7 | require "./app_database" 8 | require "../config/**" 9 | require "./models/base_model" 10 | require "./models/mixins/**" 11 | require "./models/**" 12 | require "./queries/mixins/**" 13 | require "./queries/**" 14 | require "./operations/mixins/**" 15 | require "./operations/**" 16 | require "./serializers/base_serializer" 17 | require "./serializers/**" 18 | require "./emails/base_email" 19 | require "./emails/**" 20 | require "./actions/mixins/**" 21 | require "./actions/**" 22 | require "./components/base_component" 23 | require "./components/**" 24 | require "./pages/**" 25 | require "../db/migrations/**" 26 | require "./app_server" 27 | -------------------------------------------------------------------------------- /fixtures/src_template__sec_tester/expected/src/app_database.cr: -------------------------------------------------------------------------------- 1 | class AppDatabase < Avram::Database 2 | end 3 | -------------------------------------------------------------------------------- /fixtures/src_template__sec_tester/expected/src/app_server.cr: -------------------------------------------------------------------------------- 1 | class AppServer < Lucky::BaseAppServer 2 | # Learn about middleware with HTTP::Handlers: 3 | # https://luckyframework.org/guides/http-and-routing/http-handlers 4 | def middleware : Array(HTTP::Handler) 5 | [ 6 | Lucky::RequestIdHandler.new, 7 | Lucky::ForceSSLHandler.new, 8 | Lucky::HttpMethodOverrideHandler.new, 9 | Lucky::LogHandler.new, 10 | Lucky::ErrorHandler.new(action: Errors::Show), 11 | Lucky::RemoteIpHandler.new, 12 | Lucky::RouteHandler.new, 13 | Lucky::StaticCompressionHandler.new("./public", file_ext: "gz", content_encoding: "gzip"), 14 | Lucky::StaticFileHandler.new("./public", fallthrough: false, directory_listing: false), 15 | Lucky::RouteNotFoundHandler.new, 16 | ] of HTTP::Handler 17 | end 18 | 19 | def protocol 20 | "http" 21 | end 22 | 23 | def listen 24 | server.listen(host, port, reuse_port: false) 25 | end 26 | end 27 | -------------------------------------------------------------------------------- /fixtures/src_template__sec_tester/expected/src/emails/base_email.cr: -------------------------------------------------------------------------------- 1 | # Learn about sending emails 2 | # https://luckyframework.org/guides/emails/sending-emails-with-carbon 3 | abstract class BaseEmail < Carbon::Email 4 | # You can add defaults using the 'inherited' hook 5 | # 6 | # Example: 7 | # 8 | # macro inherited 9 | # from default_from 10 | # end 11 | # 12 | # def default_from 13 | # Carbon::Address.new("support@app.com") 14 | # end 15 | end 16 | -------------------------------------------------------------------------------- /fixtures/src_template__sec_tester/expected/src/models/base_model.cr: -------------------------------------------------------------------------------- 1 | abstract class BaseModel < Avram::Model 2 | def self.database : Avram::Database.class 3 | AppDatabase 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /fixtures/src_template__sec_tester/expected/src/models/mixins/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luckyframework/lucky_cli/ccaa571765582086d437efe0da4f439a19929d64/fixtures/src_template__sec_tester/expected/src/models/mixins/.keep -------------------------------------------------------------------------------- /fixtures/src_template__sec_tester/expected/src/operations/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luckyframework/lucky_cli/ccaa571765582086d437efe0da4f439a19929d64/fixtures/src_template__sec_tester/expected/src/operations/.keep -------------------------------------------------------------------------------- /fixtures/src_template__sec_tester/expected/src/operations/mixins/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luckyframework/lucky_cli/ccaa571765582086d437efe0da4f439a19929d64/fixtures/src_template__sec_tester/expected/src/operations/mixins/.keep -------------------------------------------------------------------------------- /fixtures/src_template__sec_tester/expected/src/queries/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luckyframework/lucky_cli/ccaa571765582086d437efe0da4f439a19929d64/fixtures/src_template__sec_tester/expected/src/queries/.keep -------------------------------------------------------------------------------- /fixtures/src_template__sec_tester/expected/src/queries/mixins/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luckyframework/lucky_cli/ccaa571765582086d437efe0da4f439a19929d64/fixtures/src_template__sec_tester/expected/src/queries/mixins/.keep -------------------------------------------------------------------------------- /fixtures/src_template__sec_tester/expected/src/serializers/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luckyframework/lucky_cli/ccaa571765582086d437efe0da4f439a19929d64/fixtures/src_template__sec_tester/expected/src/serializers/.keep -------------------------------------------------------------------------------- /fixtures/src_template__sec_tester/expected/src/serializers/base_serializer.cr: -------------------------------------------------------------------------------- 1 | abstract class BaseSerializer 2 | include Lucky::Serializable 3 | 4 | def self.for_collection(collection : Enumerable, *args, **named_args) : Array(self) 5 | collection.map do |object| 6 | new(object, *args, **named_args) 7 | end 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /fixtures/src_template__sec_tester/expected/src/serializers/error_serializer.cr: -------------------------------------------------------------------------------- 1 | # This is the default error serializer generated by Lucky. 2 | # Feel free to customize it in any way you like. 3 | class ErrorSerializer < BaseSerializer 4 | def initialize( 5 | @message : String, 6 | @details : String? = nil, 7 | @param : String? = nil, # so you can track which param (if any) caused the problem 8 | ) 9 | end 10 | 11 | def render 12 | {message: @message, param: @param, details: @details} 13 | end 14 | end 15 | -------------------------------------------------------------------------------- /fixtures/src_template__sec_tester/expected/src/shards.cr: -------------------------------------------------------------------------------- 1 | # Load .env file before any other config or app code 2 | require "lucky_env" 3 | LuckyEnv.load?(".env") 4 | 5 | # Require your shards here 6 | require "lucky" 7 | require "avram/lucky" 8 | require "carbon" 9 | -------------------------------------------------------------------------------- /fixtures/src_template__sec_tester/expected/src/start_server.cr: -------------------------------------------------------------------------------- 1 | require "./app" 2 | 3 | Habitat.raise_if_missing_settings! 4 | 5 | if LuckyEnv.development? 6 | Avram::Migrator::Runner.new.ensure_migrated! 7 | Avram::SchemaEnforcer.ensure_correct_column_mappings! 8 | end 9 | 10 | app_server = AppServer.new 11 | 12 | Signal::INT.trap do 13 | app_server.close 14 | end 15 | 16 | app_server.listen 17 | -------------------------------------------------------------------------------- /fixtures/src_template__sec_tester/expected/src/test_project.cr: -------------------------------------------------------------------------------- 1 | # Typically you will not use or modify this file. 'shards build' and some 2 | # other crystal tools will sometimes use this. 3 | # 4 | # When this file is compiled/run it will require and run 'start_server', 5 | # which as its name implies will start the server for you app. 6 | require "./start_server" 7 | -------------------------------------------------------------------------------- /fixtures/src_template__sec_tester/expected/tasks.cr: -------------------------------------------------------------------------------- 1 | # This file loads your app and all your tasks when running 'lucky' 2 | # 3 | # Run 'lucky --help' to see all available tasks. 4 | # 5 | # Learn to create your own tasks: 6 | # https://luckyframework.org/guides/command-line-tasks/custom-tasks 7 | 8 | # See `LuckyEnv#task?` 9 | ENV["LUCKY_TASK"] = "true" 10 | 11 | # Load Lucky and the app (actions, models, etc.) 12 | require "./src/app" 13 | require "lucky_task" 14 | 15 | # You can add your own tasks here in the ./tasks folder 16 | require "./tasks/**" 17 | 18 | # Load migrations 19 | require "./db/migrations/**" 20 | 21 | # Load Lucky tasks (dev, routes, etc.) 22 | require "lucky/tasks/**" 23 | require "avram/lucky/tasks" 24 | 25 | LuckyTask::Runner.run 26 | -------------------------------------------------------------------------------- /fixtures/src_template__sec_tester/expected/tasks/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luckyframework/lucky_cli/ccaa571765582086d437efe0da4f439a19929d64/fixtures/src_template__sec_tester/expected/tasks/.keep -------------------------------------------------------------------------------- /fixtures/tasks.cr: -------------------------------------------------------------------------------- 1 | require "../src/lucky_cli" 2 | 3 | class PlaceholderTask < LuckyTask::Task 4 | summary "placeholder" 5 | help_message "Custom help message" 6 | switch :test_mode, "Run in test mode." 7 | 8 | def call 9 | output.puts "Calling Placeholder. Test: #{test_mode?}" 10 | end 11 | end 12 | 13 | class TaskWithInput < LuckyTask::Task 14 | summary "this should be first" 15 | 16 | def call 17 | input = gets 18 | puts "input: #{input}" 19 | end 20 | end 21 | 22 | LuckyTask::Runner.run 23 | -------------------------------------------------------------------------------- /shard.edge.yml: -------------------------------------------------------------------------------- 1 | dependencies: 2 | lucky: 3 | github: luckyframework/lucky 4 | branch: main 5 | avram: 6 | github: luckyframework/avram 7 | branch: main 8 | authentic: 9 | github: luckyframework/authentic 10 | branch: main 11 | carbon: 12 | github: luckyframework/carbon 13 | branch: main 14 | carbon_sendgrid_adapter: 15 | github: luckyframework/carbon_sendgrid_adapter 16 | branch: main 17 | lucky_env: 18 | github: luckyframework/lucky_env 19 | branch: main 20 | lucky_task: 21 | github: luckyframework/lucky_task 22 | branch: main 23 | jwt: 24 | github: crystal-community/jwt 25 | branch: master 26 | 27 | development_dependencies: 28 | lucky_flow: 29 | github: luckyframework/lucky_flow 30 | branch: main 31 | -------------------------------------------------------------------------------- /shard.override.yml: -------------------------------------------------------------------------------- 1 | # This file is used to override the shards built by 2 | # generated Lucky apps. 3 | 4 | # Uncomment if you need to override 5 | dependencies: 6 | lucky: 7 | github: luckyframework/lucky 8 | branch: main 9 | avram: 10 | github: luckyframework/avram 11 | branch: main 12 | authentic: 13 | github: luckyframework/authentic 14 | branch: main 15 | -------------------------------------------------------------------------------- /shard.yml: -------------------------------------------------------------------------------- 1 | name: lucky_cli 2 | version: 1.4.0 3 | 4 | authors: 5 | - Paul Smith 6 | 7 | targets: 8 | lucky: 9 | main: src/lucky.cr 10 | lucky.hello_world: 11 | main: fixtures/hello_world.cr 12 | 13 | crystal: ~> 1.16 14 | 15 | license: MIT 16 | 17 | dependencies: 18 | lucky_template: 19 | github: luckyframework/lucky_template 20 | version: ~> 0.2.0 21 | lucky_task: 22 | github: luckyframework/lucky_task 23 | version: ~> 0.3.0 24 | nox: 25 | github: crystal-loot/nox 26 | version: ~> 0.2.3 27 | -------------------------------------------------------------------------------- /spec/unit/api_authentication_template_spec.cr: -------------------------------------------------------------------------------- 1 | require "../spec_helper" 2 | 3 | describe ApiAuthenticationTemplate do 4 | around_each do |example| 5 | with_tempfile("tmp") do |tmp| 6 | Dir.mkdir_p(tmp) 7 | Dir.cd(tmp) do 8 | example.run 9 | end 10 | end 11 | end 12 | 13 | it "generates api authentication template" do 14 | generate_snapshot("api_authentication_template") do 15 | ApiAuthenticationTemplate.new 16 | end 17 | end 18 | end 19 | -------------------------------------------------------------------------------- /spec/unit/base_authentication_src_template_spec.cr: -------------------------------------------------------------------------------- 1 | require "../spec_helper" 2 | 3 | describe BaseAuthenticationSrcTemplate do 4 | around_each do |example| 5 | with_tempfile("tmp") do |tmp| 6 | Dir.mkdir_p(tmp) 7 | Dir.cd(tmp) do 8 | example.run 9 | end 10 | end 11 | end 12 | 13 | it "generates base authentication src template" do 14 | generate_snapshot("base_authentication_src_template") do 15 | BaseAuthenticationSrcTemplate.new 16 | end 17 | end 18 | end 19 | -------------------------------------------------------------------------------- /spec/unit/browser_authentication_src_template_spec.cr: -------------------------------------------------------------------------------- 1 | require "../spec_helper" 2 | 3 | describe BrowserAuthenticationSrcTemplate do 4 | around_each do |example| 5 | with_tempfile("tmp") do |tmp| 6 | Dir.mkdir_p(tmp) 7 | Dir.cd(tmp) do 8 | example.run 9 | end 10 | end 11 | end 12 | 13 | it "generates browser authentication src template" do 14 | generate_snapshot("browser_authentication_src_template") do 15 | BrowserAuthenticationSrcTemplate.new 16 | end 17 | end 18 | end 19 | -------------------------------------------------------------------------------- /spec/unit/browser_src_template_spec.cr: -------------------------------------------------------------------------------- 1 | require "../spec_helper" 2 | 3 | describe BrowserSrcTemplate do 4 | around_each do |example| 5 | with_tempfile("tmp") do |tmp| 6 | Dir.mkdir_p(tmp) 7 | Dir.cd(tmp) do 8 | example.run 9 | end 10 | end 11 | end 12 | 13 | it "generates browser src template" do 14 | generate_snapshot("browser_src_template") do 15 | BrowserSrcTemplate.new(generate_auth: true) 16 | end 17 | end 18 | 19 | it "generates browser src template without generate auth" do 20 | generate_snapshot("browser_src_template__generate_auth") do 21 | BrowserSrcTemplate.new(generate_auth: false) 22 | end 23 | end 24 | end 25 | -------------------------------------------------------------------------------- /src/api_authentication_app_skeleton/spec/requests/api/me/show_spec.cr.ecr: -------------------------------------------------------------------------------- 1 | require "../../../spec_helper" 2 | 3 | describe Api::Me::Show do 4 | it "returns the signed in user" do 5 | user = UserFactory.create 6 | 7 | response = ApiClient.auth(user).exec(Api::Me::Show) 8 | 9 | response.should send_json(200, email: user.email) 10 | end 11 | 12 | it "fails if not authenticated" do 13 | response = ApiClient.exec(Api::Me::Show) 14 | 15 | response.status_code.should eq(401) 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /src/api_authentication_app_skeleton/spec/requests/api/sign_ins/create_spec.cr.ecr: -------------------------------------------------------------------------------- 1 | require "../../../spec_helper" 2 | 3 | describe Api::SignIns::Create do 4 | it "returns a token" do 5 | UserToken.stub_token("fake-token") do 6 | user = UserFactory.create 7 | 8 | response = ApiClient.exec(Api::SignIns::Create, user: valid_params(user)) 9 | 10 | response.should send_json(200, token: "fake-token") 11 | end 12 | end 13 | 14 | it "returns an error if credentials are invalid" do 15 | user = UserFactory.create 16 | invalid_params = valid_params(user).merge(password: "incorrect") 17 | 18 | response = ApiClient.exec(Api::SignIns::Create, user: invalid_params) 19 | 20 | response.should send_json( 21 | 400, 22 | param: "password", 23 | details: "password is wrong" 24 | ) 25 | end 26 | end 27 | 28 | private def valid_params(user : User) 29 | { 30 | email: user.email, 31 | password: "password", 32 | } 33 | end 34 | -------------------------------------------------------------------------------- /src/api_authentication_app_skeleton/src/actions/api/me/show.cr.ecr: -------------------------------------------------------------------------------- 1 | class Api::Me::Show < ApiAction 2 | get "/api/me" do 3 | json UserSerializer.new(current_user) 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /src/api_authentication_app_skeleton/src/actions/api/sign_ins/create.cr.ecr: -------------------------------------------------------------------------------- 1 | class Api::SignIns::Create < ApiAction 2 | include Api::Auth::SkipRequireAuthToken 3 | 4 | post "/api/sign_ins" do 5 | SignInUser.run(params) do |operation, user| 6 | if user 7 | json({token: UserToken.generate(user)}) 8 | else 9 | raise Avram::InvalidOperationError.new(operation) 10 | end 11 | end 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /src/api_authentication_app_skeleton/src/actions/api/sign_ups/create.cr.ecr: -------------------------------------------------------------------------------- 1 | class Api::SignUps::Create < ApiAction 2 | include Api::Auth::SkipRequireAuthToken 3 | 4 | post "/api/sign_ups" do 5 | user = SignUpUser.create!(params) 6 | json({token: UserToken.generate(user)}) 7 | end 8 | end 9 | -------------------------------------------------------------------------------- /src/api_authentication_app_skeleton/src/actions/mixins/api/auth/helpers.cr.ecr: -------------------------------------------------------------------------------- 1 | module Api::Auth::Helpers 2 | # The 'memoize' macro makes sure only one query is issued to find the user 3 | memoize def current_user? : User? 4 | auth_token.try do |value| 5 | user_from_auth_token(value) 6 | end 7 | end 8 | 9 | private def auth_token : String? 10 | bearer_token || token_param 11 | end 12 | 13 | private def bearer_token : String? 14 | context.request.headers["Authorization"]? 15 | .try(&.gsub("Bearer", "")) 16 | .try(&.strip) 17 | end 18 | 19 | private def token_param : String? 20 | params.get?(:auth_token) 21 | end 22 | 23 | private def user_from_auth_token(token : String) : User? 24 | UserToken.decode_user_id(token).try do |user_id| 25 | UserQuery.new.id(user_id).first? 26 | end 27 | end 28 | end 29 | -------------------------------------------------------------------------------- /src/api_authentication_app_skeleton/src/actions/mixins/api/auth/skip_require_auth_token.cr.ecr: -------------------------------------------------------------------------------- 1 | module Api::Auth::SkipRequireAuthToken 2 | macro included 3 | skip require_auth_token 4 | end 5 | 6 | # Since sign in is not required, current_user might be nil 7 | def current_user : User? 8 | current_user? 9 | end 10 | end 11 | -------------------------------------------------------------------------------- /src/api_authentication_app_skeleton/src/serializers/user_serializer.cr.ecr: -------------------------------------------------------------------------------- 1 | class UserSerializer < BaseSerializer 2 | def initialize(@user : User) 3 | end 4 | 5 | def render 6 | {email: @user.email} 7 | end 8 | end 9 | -------------------------------------------------------------------------------- /src/app_with_sec_tester/spec/setup/sec_tester.cr.ecr: -------------------------------------------------------------------------------- 1 | require "lucky_sec_tester" 2 | 3 | # Signup for a `BRIGHT_TOKEN` at 4 | # [Bright Security](https://app.neuralegion.com/signup) 5 | # Read more about the SecTester on https://github.com/luckyframework/lucky_sec_tester 6 | LuckySecTester.configure do |setting| 7 | setting.bright_token = ENV["BRIGHT_TOKEN"] 8 | setting.project_id = ENV["BRIGHT_PROJECT_ID"] 9 | end 10 | -------------------------------------------------------------------------------- /src/base_authentication_app_skeleton/config/authentic.cr.ecr: -------------------------------------------------------------------------------- 1 | require "./server" 2 | 3 | Authentic.configure do |settings| 4 | settings.secret_key = Lucky::Server.settings.secret_key_base 5 | 6 | unless LuckyEnv.production? 7 | # This value can be between 4 and 31 8 | fastest_encryption_possible = 4 9 | settings.encryption_cost = fastest_encryption_possible 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /src/base_authentication_app_skeleton/db/migrations/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luckyframework/lucky_cli/ccaa571765582086d437efe0da4f439a19929d64/src/base_authentication_app_skeleton/db/migrations/.keep -------------------------------------------------------------------------------- /src/base_authentication_app_skeleton/db/migrations/00000000000001_create_users.cr.ecr: -------------------------------------------------------------------------------- 1 | class CreateUsers::V00000000000001 < Avram::Migrator::Migration::V1 2 | def migrate 3 | enable_extension "citext" 4 | 5 | create table_for(User) do 6 | primary_key id : Int64 7 | add_timestamps 8 | add email : String, unique: true, case_sensitive: false 9 | add encrypted_password : String 10 | end 11 | end 12 | 13 | def rollback 14 | drop table_for(User) 15 | disable_extension "citext" 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /src/base_authentication_app_skeleton/spec/support/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luckyframework/lucky_cli/ccaa571765582086d437efe0da4f439a19929d64/src/base_authentication_app_skeleton/spec/support/.keep -------------------------------------------------------------------------------- /src/base_authentication_app_skeleton/spec/support/factories/user_factory.cr.ecr: -------------------------------------------------------------------------------- 1 | class UserFactory < Avram::Factory 2 | def initialize 3 | email "#{sequence("test-email")}@example.com" 4 | encrypted_password Authentic.generate_encrypted_password("password") 5 | end 6 | end 7 | -------------------------------------------------------------------------------- /src/base_authentication_app_skeleton/src/models/user.cr.ecr: -------------------------------------------------------------------------------- 1 | class User < BaseModel 2 | include Carbon::Emailable 3 | include Authentic::PasswordAuthenticatable 4 | 5 | table do 6 | column email : String 7 | column encrypted_password : String 8 | end 9 | 10 | def emailable : Carbon::Address 11 | Carbon::Address.new(email) 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /src/base_authentication_app_skeleton/src/operations/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luckyframework/lucky_cli/ccaa571765582086d437efe0da4f439a19929d64/src/base_authentication_app_skeleton/src/operations/.keep -------------------------------------------------------------------------------- /src/base_authentication_app_skeleton/src/operations/mixins/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luckyframework/lucky_cli/ccaa571765582086d437efe0da4f439a19929d64/src/base_authentication_app_skeleton/src/operations/mixins/.keep -------------------------------------------------------------------------------- /src/base_authentication_app_skeleton/src/operations/mixins/password_validations.cr.ecr: -------------------------------------------------------------------------------- 1 | module PasswordValidations 2 | macro included 3 | before_save run_password_validations 4 | end 5 | 6 | private def run_password_validations 7 | validate_required password, password_confirmation 8 | validate_confirmation_of password, with: password_confirmation 9 | # 72 is a limitation of BCrypt 10 | validate_size_of password, min: 6, max: 72 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /src/base_authentication_app_skeleton/src/operations/mixins/user_from_email.cr.ecr: -------------------------------------------------------------------------------- 1 | module UserFromEmail 2 | private def user_from_email : User? 3 | email.value.try do |value| 4 | UserQuery.new.email(value).first? 5 | end 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /src/base_authentication_app_skeleton/src/operations/request_password_reset.cr.ecr: -------------------------------------------------------------------------------- 1 | class RequestPasswordReset < Avram::Operation 2 | # You can modify this in src/operations/mixins/user_from_email.cr 3 | include UserFromEmail 4 | 5 | attribute email : String 6 | 7 | # Run validations and yield the operation and the user if valid 8 | def run 9 | user = user_from_email 10 | validate(user) 11 | 12 | if valid? 13 | user 14 | else 15 | nil 16 | end 17 | end 18 | 19 | def validate(user : User?) 20 | validate_required email 21 | if user.nil? 22 | email.add_error "is not in our system" 23 | end 24 | end 25 | end 26 | -------------------------------------------------------------------------------- /src/base_authentication_app_skeleton/src/operations/reset_password.cr.ecr: -------------------------------------------------------------------------------- 1 | class ResetPassword < User::SaveOperation 2 | # Change password validations in src/operations/mixins/password_validations.cr 3 | include PasswordValidations 4 | 5 | attribute password : String 6 | attribute password_confirmation : String 7 | 8 | before_save do 9 | Authentic.copy_and_encrypt password, to: encrypted_password 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /src/base_authentication_app_skeleton/src/operations/sign_up_user.cr.ecr: -------------------------------------------------------------------------------- 1 | class SignUpUser < User::SaveOperation 2 | param_key :user 3 | # Change password validations in src/operations/mixins/password_validations.cr 4 | include PasswordValidations 5 | 6 | permit_columns email 7 | attribute password : String 8 | attribute password_confirmation : String 9 | 10 | before_save do 11 | validate_uniqueness_of email 12 | Authentic.copy_and_encrypt(password, to: encrypted_password) if password.valid? 13 | end 14 | end 15 | -------------------------------------------------------------------------------- /src/base_authentication_app_skeleton/src/queries/user_query.cr.ecr: -------------------------------------------------------------------------------- 1 | class UserQuery < User::BaseQuery 2 | end 3 | -------------------------------------------------------------------------------- /src/browser_app_skeleton/bs-config.js.ecr: -------------------------------------------------------------------------------- 1 | /* 2 | | Browser-sync config file 3 | | 4 | | For up-to-date information about the options: 5 | | http://www.browsersync.io/docs/options/ 6 | | 7 | */ 8 | 9 | module.exports = { 10 | snippetOptions: { 11 | rule: { 12 | match: /<\/head>/i, 13 | fn: function (snippet, match) { 14 | return snippet + match; 15 | } 16 | } 17 | }, 18 | files: ["public/css/**/*.css", "public/js/**/*.js"], 19 | watchEvents: ["change"], 20 | open: false, 21 | browser: "default", 22 | ghostMode: false, 23 | ui: false, 24 | online: false, 25 | logConnections: false 26 | }; 27 | -------------------------------------------------------------------------------- /src/browser_app_skeleton/config/html_page.cr.ecr: -------------------------------------------------------------------------------- 1 | Lucky::HTMLPage.configure do |settings| 2 | settings.render_component_comments = !LuckyEnv.production? 3 | end 4 | -------------------------------------------------------------------------------- /src/browser_app_skeleton/db/migrations/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luckyframework/lucky_cli/ccaa571765582086d437efe0da4f439a19929d64/src/browser_app_skeleton/db/migrations/.keep -------------------------------------------------------------------------------- /src/browser_app_skeleton/package.json.ecr: -------------------------------------------------------------------------------- 1 | { 2 | "license": "UNLICENSED", 3 | "private": true, 4 | "dependencies": { 5 | "@rails/ujs": "^7.1.0", 6 | "modern-normalize": "^2.0.0" 7 | }, 8 | "scripts": { 9 | "heroku-postbuild": "yarn prod", 10 | "dev": "yarn run mix", 11 | "watch": "yarn run mix watch", 12 | "prod": "yarn run mix --production" 13 | }, 14 | "devDependencies": { 15 | "@babel/compat-data": "^7.23.5", 16 | "browser-sync": "^2.29.3", 17 | "compression-webpack-plugin": "^10.0.0", 18 | "laravel-mix": "^6.0.49", 19 | "postcss": "^8.4.32", 20 | "resolve-url-loader": "^5.0.0", 21 | "sass": "^1.69.5", 22 | "sass-loader": "^13.3.2" 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/browser_app_skeleton/public/assets/images/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luckyframework/lucky_cli/ccaa571765582086d437efe0da4f439a19929d64/src/browser_app_skeleton/public/assets/images/.keep -------------------------------------------------------------------------------- /src/browser_app_skeleton/public/favicon.ico.ecr: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luckyframework/lucky_cli/ccaa571765582086d437efe0da4f439a19929d64/src/browser_app_skeleton/public/favicon.ico.ecr -------------------------------------------------------------------------------- /src/browser_app_skeleton/public/mix-manifest.json.ecr: -------------------------------------------------------------------------------- 1 | { 2 | "/css/app.css": "/css/app.css", 3 | "/js/app.js": "/js/app.js" 4 | } 5 | -------------------------------------------------------------------------------- /src/browser_app_skeleton/public/robots.txt.ecr: -------------------------------------------------------------------------------- 1 | # Learn more about robots.txt: https://www.robotstxt.org/robotstxt.html 2 | User-agent: * 3 | # 'Disallow' with an empty value allows all paths to be crawled 4 | Disallow: 5 | -------------------------------------------------------------------------------- /src/browser_app_skeleton/spec/flows/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luckyframework/lucky_cli/ccaa571765582086d437efe0da4f439a19929d64/src/browser_app_skeleton/spec/flows/.keep -------------------------------------------------------------------------------- /src/browser_app_skeleton/spec/setup/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luckyframework/lucky_cli/ccaa571765582086d437efe0da4f439a19929d64/src/browser_app_skeleton/spec/setup/.keep -------------------------------------------------------------------------------- /src/browser_app_skeleton/spec/support/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luckyframework/lucky_cli/ccaa571765582086d437efe0da4f439a19929d64/src/browser_app_skeleton/spec/support/.keep -------------------------------------------------------------------------------- /src/browser_app_skeleton/spec/support/factories/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luckyframework/lucky_cli/ccaa571765582086d437efe0da4f439a19929d64/src/browser_app_skeleton/spec/support/factories/.keep -------------------------------------------------------------------------------- /src/browser_app_skeleton/spec/support/flows/base_flow.cr.ecr: -------------------------------------------------------------------------------- 1 | # Add methods that all or most Flows need to share 2 | class BaseFlow < LuckyFlow 3 | end 4 | -------------------------------------------------------------------------------- /src/browser_app_skeleton/src/components/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luckyframework/lucky_cli/ccaa571765582086d437efe0da4f439a19929d64/src/browser_app_skeleton/src/components/.keep -------------------------------------------------------------------------------- /src/browser_app_skeleton/src/components/base_component.cr.ecr: -------------------------------------------------------------------------------- 1 | abstract class BaseComponent < Lucky::BaseComponent 2 | end 3 | -------------------------------------------------------------------------------- /src/browser_app_skeleton/src/components/shared/field_errors.cr.ecr: -------------------------------------------------------------------------------- 1 | class Shared::FieldErrors(T) < BaseComponent 2 | needs attribute : Avram::PermittedAttribute(T) 3 | 4 | # Customize the markup and styles to match your application 5 | def render 6 | unless attribute.valid? 7 | div class: "error" do 8 | text "#{label_text} #{attribute.errors.first}" 9 | end 10 | end 11 | end 12 | 13 | def label_text : String 14 | Wordsmith::Inflector.humanize(attribute.name.to_s) 15 | end 16 | end 17 | -------------------------------------------------------------------------------- /src/browser_app_skeleton/src/components/shared/flash_messages.cr.ecr: -------------------------------------------------------------------------------- 1 | class Shared::FlashMessages < BaseComponent 2 | needs flash : Lucky::FlashStore 3 | 4 | def render 5 | flash.each do |flash_type, flash_message| 6 | div class: "flash-#{flash_type}", flow_id: "flash" do 7 | text flash_message 8 | end 9 | end 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /src/browser_app_skeleton/src/components/shared/layout_head.cr.ecr: -------------------------------------------------------------------------------- 1 | class Shared::LayoutHead < BaseComponent 2 | needs page_title : String 3 | 4 | def render 5 | head do 6 | utf8_charset 7 | title "My App - #{@page_title}" 8 | css_link asset("css/app.css") 9 | js_link asset("js/app.js"), defer: "true" 10 | csrf_meta_tags 11 | responsive_meta_tag 12 | 13 | # Development helper used with the `lucky watch` command. 14 | # Reloads the browser when files are updated. 15 | live_reload_connect_tag if LuckyEnv.development? 16 | end 17 | end 18 | end 19 | -------------------------------------------------------------------------------- /src/browser_app_skeleton/src/css/components/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luckyframework/lucky_cli/ccaa571765582086d437efe0da4f439a19929d64/src/browser_app_skeleton/src/css/components/.keep -------------------------------------------------------------------------------- /src/browser_app_skeleton/src/css/mixins/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luckyframework/lucky_cli/ccaa571765582086d437efe0da4f439a19929d64/src/browser_app_skeleton/src/css/mixins/.keep -------------------------------------------------------------------------------- /src/browser_app_skeleton/src/css/variables/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luckyframework/lucky_cli/ccaa571765582086d437efe0da4f439a19929d64/src/browser_app_skeleton/src/css/variables/.keep -------------------------------------------------------------------------------- /src/browser_app_skeleton/src/emails/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luckyframework/lucky_cli/ccaa571765582086d437efe0da4f439a19929d64/src/browser_app_skeleton/src/emails/.keep -------------------------------------------------------------------------------- /src/browser_app_skeleton/src/js/app.js.ecr: -------------------------------------------------------------------------------- 1 | /* eslint no-console:0 */ 2 | 3 | // Rails Unobtrusive JavaScript (UJS) is *required* for links in Lucky that use DELETE, POST and PUT. 4 | // Though it says "Rails" it actually works with any framework. 5 | import Rails from "@rails/ujs"; 6 | Rails.start(); 7 | 8 | -------------------------------------------------------------------------------- /src/browser_app_skeleton/src/models/mixins/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luckyframework/lucky_cli/ccaa571765582086d437efe0da4f439a19929d64/src/browser_app_skeleton/src/models/mixins/.keep -------------------------------------------------------------------------------- /src/browser_app_skeleton/src/operations/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luckyframework/lucky_cli/ccaa571765582086d437efe0da4f439a19929d64/src/browser_app_skeleton/src/operations/.keep -------------------------------------------------------------------------------- /src/browser_app_skeleton/src/operations/mixins/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luckyframework/lucky_cli/ccaa571765582086d437efe0da4f439a19929d64/src/browser_app_skeleton/src/operations/mixins/.keep -------------------------------------------------------------------------------- /src/browser_app_skeleton/src/pages/main_layout.cr.ecr: -------------------------------------------------------------------------------- 1 | abstract class MainLayout 2 | include Lucky::HTMLPage 3 | 4 | abstract def content 5 | abstract def page_title 6 | 7 | # The default page title. It is passed to `Shared::LayoutHead`. 8 | # 9 | # Add a `page_title` method to pages to override it. You can also remove 10 | # This method so every page is required to have its own page title. 11 | def page_title 12 | "Welcome" 13 | end 14 | 15 | def render 16 | html_doctype 17 | 18 | html lang: "en" do 19 | mount Shared::LayoutHead, page_title: page_title 20 | 21 | body do 22 | mount Shared::FlashMessages, context.flash 23 | content 24 | end 25 | end 26 | end 27 | end 28 | -------------------------------------------------------------------------------- /src/browser_authentication_app_skeleton/spec/flows/reset_password_spec.cr.ecr: -------------------------------------------------------------------------------- 1 | require "../spec_helper" 2 | 3 | describe "Reset password flow", tags: "flow" do 4 | it "works" do 5 | user = UserFactory.create 6 | flow = ResetPasswordFlow.new(user) 7 | 8 | flow.request_password_reset 9 | flow.should_have_sent_reset_email 10 | flow.reset_password "new-password" 11 | flow.should_be_signed_in 12 | flow.sign_out 13 | flow.sign_in "wrong-password" 14 | flow.should_have_password_error 15 | flow.sign_in "new-password" 16 | flow.should_be_signed_in 17 | end 18 | end 19 | -------------------------------------------------------------------------------- /src/browser_authentication_app_skeleton/spec/support/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luckyframework/lucky_cli/ccaa571765582086d437efe0da4f439a19929d64/src/browser_authentication_app_skeleton/spec/support/.keep -------------------------------------------------------------------------------- /src/browser_authentication_app_skeleton/src/actions/me/show.cr.ecr: -------------------------------------------------------------------------------- 1 | class Me::Show < BrowserAction 2 | get "/me" do 3 | html ShowPage 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /src/browser_authentication_app_skeleton/src/actions/mixins/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luckyframework/lucky_cli/ccaa571765582086d437efe0da4f439a19929d64/src/browser_authentication_app_skeleton/src/actions/mixins/.keep -------------------------------------------------------------------------------- /src/browser_authentication_app_skeleton/src/actions/mixins/auth/allow_guests.cr.ecr: -------------------------------------------------------------------------------- 1 | module Auth::AllowGuests 2 | macro included 3 | skip require_sign_in 4 | end 5 | 6 | # Since sign in is not required, current_user might be nil 7 | def current_user : User? 8 | current_user? 9 | end 10 | end 11 | -------------------------------------------------------------------------------- /src/browser_authentication_app_skeleton/src/actions/mixins/auth/password_resets/base.cr.ecr: -------------------------------------------------------------------------------- 1 | module Auth::PasswordResets::Base 2 | macro included 3 | include Auth::RedirectSignedInUsers 4 | include Auth::PasswordResets::FindUser 5 | include Auth::PasswordResets::RequireToken 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /src/browser_authentication_app_skeleton/src/actions/mixins/auth/password_resets/find_user.cr.ecr: -------------------------------------------------------------------------------- 1 | module Auth::PasswordResets::FindUser 2 | private def user : User 3 | UserQuery.find(user_id) 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /src/browser_authentication_app_skeleton/src/actions/mixins/auth/password_resets/require_token.cr.ecr: -------------------------------------------------------------------------------- 1 | module Auth::PasswordResets::RequireToken 2 | macro included 3 | before require_valid_password_reset_token 4 | end 5 | 6 | abstract def token : String 7 | abstract def user : User 8 | 9 | private def require_valid_password_reset_token 10 | if Authentic.valid_password_reset_token?(user, token) 11 | continue 12 | else 13 | flash.failure = "The password reset link is incorrect or expired. Please try again." 14 | redirect to: PasswordResetRequests::New 15 | end 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /src/browser_authentication_app_skeleton/src/actions/mixins/auth/password_resets/token_from_session.cr.ecr: -------------------------------------------------------------------------------- 1 | module Auth::PasswordResets::TokenFromSession 2 | private def token : String 3 | session.get?(:password_reset_token) || raise "Password reset token not found in session" 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /src/browser_authentication_app_skeleton/src/actions/mixins/auth/redirect_signed_in_users.cr.ecr: -------------------------------------------------------------------------------- 1 | module Auth::RedirectSignedInUsers 2 | macro included 3 | include Auth::AllowGuests 4 | before redirect_signed_in_users 5 | end 6 | 7 | private def redirect_signed_in_users 8 | if current_user? 9 | flash.success = "You are already signed in" 10 | redirect to: Home::Index 11 | else 12 | continue 13 | end 14 | end 15 | 16 | # current_user returns nil because signed in users are redirected. 17 | def current_user 18 | end 19 | end 20 | -------------------------------------------------------------------------------- /src/browser_authentication_app_skeleton/src/actions/mixins/auth/require_sign_in.cr.ecr: -------------------------------------------------------------------------------- 1 | module Auth::RequireSignIn 2 | macro included 3 | before require_sign_in 4 | end 5 | 6 | private def require_sign_in 7 | if current_user? 8 | continue 9 | else 10 | Authentic.remember_requested_path(self) 11 | flash.info = "Please sign in first" 12 | redirect to: SignIns::New 13 | end 14 | end 15 | 16 | # Tells the compiler that the current_user is not nil since we have checked 17 | # that the user is signed in 18 | private def current_user : User 19 | current_user?.as(User) 20 | end 21 | end 22 | -------------------------------------------------------------------------------- /src/browser_authentication_app_skeleton/src/actions/mixins/auth/test_backdoor.cr.ecr: -------------------------------------------------------------------------------- 1 | module Auth::TestBackdoor 2 | macro included 3 | before test_backdoor 4 | end 5 | 6 | private def test_backdoor 7 | if LuckyEnv.test? && (user_id = params.get?(:backdoor_user_id)) 8 | user = UserQuery.find(user_id) 9 | sign_in user 10 | end 11 | continue 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /src/browser_authentication_app_skeleton/src/actions/password_reset_requests/create.cr.ecr: -------------------------------------------------------------------------------- 1 | class PasswordResetRequests::Create < BrowserAction 2 | include Auth::RedirectSignedInUsers 3 | 4 | post "/password_reset_requests" do 5 | RequestPasswordReset.run(params) do |operation, user| 6 | if user 7 | PasswordResetRequestEmail.new(user).deliver 8 | flash.success = "You should receive an email on how to reset your password shortly" 9 | redirect SignIns::New 10 | else 11 | html NewPage, operation: operation 12 | end 13 | end 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /src/browser_authentication_app_skeleton/src/actions/password_reset_requests/new.cr.ecr: -------------------------------------------------------------------------------- 1 | class PasswordResetRequests::New < BrowserAction 2 | include Auth::RedirectSignedInUsers 3 | 4 | get "/password_reset_requests/new" do 5 | html NewPage, operation: RequestPasswordReset.new 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /src/browser_authentication_app_skeleton/src/actions/password_resets/create.cr.ecr: -------------------------------------------------------------------------------- 1 | class PasswordResets::Create < BrowserAction 2 | include Auth::PasswordResets::Base 3 | include Auth::PasswordResets::TokenFromSession 4 | 5 | post "/password_resets/:user_id" do 6 | ResetPassword.update(user, params) do |operation, user| 7 | if operation.saved? 8 | session.delete(:password_reset_token) 9 | sign_in user 10 | flash.success = "Your password has been reset" 11 | redirect to: Home::Index 12 | else 13 | html NewPage, operation: operation, user_id: user_id.to_i64 14 | end 15 | end 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /src/browser_authentication_app_skeleton/src/actions/password_resets/edit.cr.ecr: -------------------------------------------------------------------------------- 1 | class PasswordResets::Edit < BrowserAction 2 | include Auth::PasswordResets::Base 3 | include Auth::PasswordResets::TokenFromSession 4 | 5 | get "/password_resets/:user_id/edit" do 6 | html NewPage, operation: ResetPassword.new, user_id: user_id.to_i64 7 | end 8 | end 9 | -------------------------------------------------------------------------------- /src/browser_authentication_app_skeleton/src/actions/password_resets/new.cr.ecr: -------------------------------------------------------------------------------- 1 | class PasswordResets::New < BrowserAction 2 | include Auth::PasswordResets::Base 3 | 4 | param token : String 5 | 6 | get "/password_resets/:user_id" do 7 | redirect_to_edit_form_without_token_param 8 | end 9 | 10 | # This is to prevent password reset tokens from being scraped in the HTTP Referer header 11 | # See more info here: https://github.com/thoughtbot/clearance/pull/707 12 | private def redirect_to_edit_form_without_token_param 13 | make_token_available_to_future_actions 14 | redirect to: PasswordResets::Edit.with(user_id) 15 | end 16 | 17 | private def make_token_available_to_future_actions 18 | session.set(:password_reset_token, token) 19 | end 20 | end 21 | -------------------------------------------------------------------------------- /src/browser_authentication_app_skeleton/src/actions/sign_ins/create.cr.ecr: -------------------------------------------------------------------------------- 1 | class SignIns::Create < BrowserAction 2 | include Auth::RedirectSignedInUsers 3 | 4 | post "/sign_in" do 5 | SignInUser.run(params) do |operation, authenticated_user| 6 | if authenticated_user 7 | sign_in(authenticated_user) 8 | flash.success = "You're now signed in" 9 | Authentic.redirect_to_originally_requested_path(self, fallback: Home::Index) 10 | else 11 | flash.failure = "Sign in failed" 12 | html NewPage, operation: operation 13 | end 14 | end 15 | end 16 | end 17 | -------------------------------------------------------------------------------- /src/browser_authentication_app_skeleton/src/actions/sign_ins/delete.cr.ecr: -------------------------------------------------------------------------------- 1 | class SignIns::Delete < BrowserAction 2 | delete "/sign_out" do 3 | sign_out 4 | flash.info = "You have been signed out" 5 | redirect to: SignIns::New 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /src/browser_authentication_app_skeleton/src/actions/sign_ins/new.cr.ecr: -------------------------------------------------------------------------------- 1 | class SignIns::New < BrowserAction 2 | include Auth::RedirectSignedInUsers 3 | 4 | get "/sign_in" do 5 | html NewPage, operation: SignInUser.new 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /src/browser_authentication_app_skeleton/src/actions/sign_ups/create.cr.ecr: -------------------------------------------------------------------------------- 1 | class SignUps::Create < BrowserAction 2 | include Auth::RedirectSignedInUsers 3 | 4 | post "/sign_up" do 5 | SignUpUser.create(params) do |operation, user| 6 | if user 7 | flash.info = "Thanks for signing up" 8 | sign_in(user) 9 | redirect to: Home::Index 10 | else 11 | flash.info = "Couldn't sign you up" 12 | html NewPage, operation: operation 13 | end 14 | end 15 | end 16 | end 17 | -------------------------------------------------------------------------------- /src/browser_authentication_app_skeleton/src/actions/sign_ups/new.cr.ecr: -------------------------------------------------------------------------------- 1 | class SignUps::New < BrowserAction 2 | include Auth::RedirectSignedInUsers 3 | 4 | get "/sign_up" do 5 | html NewPage, operation: SignUpUser.new 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /src/browser_authentication_app_skeleton/src/emails/password_reset_request_email.cr.ecr: -------------------------------------------------------------------------------- 1 | class PasswordResetRequestEmail < BaseEmail 2 | Habitat.create { setting stubbed_token : String? } 3 | delegate stubbed_token, to: :settings 4 | 5 | def initialize(@user : User) 6 | @token = stubbed_token || Authentic.generate_password_reset_token(@user) 7 | end 8 | 9 | to @user 10 | from "myapp@support.com" # or set a default in src/emails/base_email.cr 11 | subject "Reset your password" 12 | templates html, text 13 | end 14 | -------------------------------------------------------------------------------- /src/browser_authentication_app_skeleton/src/emails/templates/password_reset_request_email/html.ecr.ecr: -------------------------------------------------------------------------------- 1 |

Please reset your password

2 | 3 | %= PasswordResets::New.url(@user.id, @token) %>">Reset password 4 | -------------------------------------------------------------------------------- /src/browser_authentication_app_skeleton/src/emails/templates/password_reset_request_email/text.ecr.ecr: -------------------------------------------------------------------------------- 1 | Please reset your password: 2 | 3 | <%="<"%>%= PasswordResets::New.url(@user.id, @token) %> 4 | -------------------------------------------------------------------------------- /src/browser_authentication_app_skeleton/src/pages/auth_layout.cr.ecr: -------------------------------------------------------------------------------- 1 | abstract class AuthLayout 2 | include Lucky::HTMLPage 3 | 4 | abstract def content 5 | abstract def page_title 6 | 7 | # The default page title. It is passed to `Shared::LayoutHead`. 8 | # 9 | # Add a `page_title` method to pages to override it. You can also remove 10 | # This method so every page is required to have its own page title. 11 | def page_title 12 | "Welcome" 13 | end 14 | 15 | def render 16 | html_doctype 17 | 18 | html lang: "en" do 19 | mount Shared::LayoutHead, page_title: page_title 20 | 21 | body do 22 | mount Shared::FlashMessages, context.flash 23 | content 24 | end 25 | end 26 | end 27 | end 28 | -------------------------------------------------------------------------------- /src/browser_authentication_app_skeleton/src/pages/me/show_page.cr.ecr: -------------------------------------------------------------------------------- 1 | class Me::ShowPage < MainLayout 2 | def content 3 | h1 "This is your profile" 4 | h3 "Email: #{@current_user.email}" 5 | helpful_tips 6 | end 7 | 8 | private def helpful_tips 9 | h3 "Next, you may want to:" 10 | ul do 11 | li { link_to_authentication_guides } 12 | li "Modify this page: src/pages/me/show_page.cr" 13 | li "Change where you go after sign in: src/actions/home/index.cr" 14 | end 15 | end 16 | 17 | private def link_to_authentication_guides 18 | a "Check out the authentication guides", 19 | href: "https://luckyframework.org/guides/authentication" 20 | end 21 | end 22 | -------------------------------------------------------------------------------- /src/browser_authentication_app_skeleton/src/pages/password_reset_requests/new_page.cr.ecr: -------------------------------------------------------------------------------- 1 | class PasswordResetRequests::NewPage < AuthLayout 2 | needs operation : RequestPasswordReset 3 | 4 | def content 5 | h1 "Reset your password" 6 | render_form(@operation) 7 | end 8 | 9 | private def render_form(op) 10 | form_for PasswordResetRequests::Create do 11 | mount Shared::Field, attribute: op.email, label_text: "Email", &.email_input 12 | submit "Reset Password", flow_id: "request-password-reset-button" 13 | end 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /src/browser_authentication_app_skeleton/src/pages/password_resets/new_page.cr.ecr: -------------------------------------------------------------------------------- 1 | class PasswordResets::NewPage < AuthLayout 2 | needs operation : ResetPassword 3 | needs user_id : Int64 4 | 5 | def content 6 | h1 "Reset your password" 7 | render_password_reset_form(@operation) 8 | end 9 | 10 | private def render_password_reset_form(op) 11 | form_for PasswordResets::Create.with(@user_id) do 12 | mount Shared::Field, attribute: op.password, label_text: "Password", &.password_input(autofocus: "true") 13 | mount Shared::Field, attribute: op.password_confirmation, label_text: "Confirm Password", &.password_input 14 | 15 | submit "Update Password", flow_id: "update-password-button" 16 | end 17 | end 18 | end 19 | -------------------------------------------------------------------------------- /src/browser_authentication_app_skeleton/src/pages/sign_ins/new_page.cr.ecr: -------------------------------------------------------------------------------- 1 | class SignIns::NewPage < AuthLayout 2 | needs operation : SignInUser 3 | 4 | def content 5 | h1 "Sign In" 6 | render_sign_in_form(@operation) 7 | end 8 | 9 | private def render_sign_in_form(op) 10 | form_for SignIns::Create do 11 | sign_in_fields(op) 12 | submit "Sign In", flow_id: "sign-in-button" 13 | end 14 | link "Reset password", to: PasswordResetRequests::New 15 | text " | " 16 | link "Sign up", to: SignUps::New 17 | end 18 | 19 | private def sign_in_fields(op) 20 | mount Shared::Field, attribute: op.email, label_text: "Email", &.email_input(autofocus: "true") 21 | mount Shared::Field, attribute: op.password, label_text: "Password", &.password_input 22 | end 23 | end 24 | -------------------------------------------------------------------------------- /src/browser_authentication_app_skeleton/src/pages/sign_ups/new_page.cr.ecr: -------------------------------------------------------------------------------- 1 | class SignUps::NewPage < AuthLayout 2 | needs operation : SignUpUser 3 | 4 | def content 5 | h1 "Sign Up" 6 | render_sign_up_form(@operation) 7 | end 8 | 9 | private def render_sign_up_form(op) 10 | form_for SignUps::Create do 11 | sign_up_fields(op) 12 | submit "Sign Up", flow_id: "sign-up-button" 13 | end 14 | link "Sign in instead", to: SignIns::New 15 | end 16 | 17 | private def sign_up_fields(op) 18 | mount Shared::Field, attribute: op.email, label_text: "Email", &.email_input(autofocus: "true") 19 | mount Shared::Field, attribute: op.password, label_text: "Password", &.password_input 20 | mount Shared::Field, attribute: op.password_confirmation, label_text: "Confirm Password", &.password_input 21 | end 22 | end 23 | -------------------------------------------------------------------------------- /src/dev.cr: -------------------------------------------------------------------------------- 1 | class LuckyCli::Dev < LuckyTask::Task 2 | summary "Starts your app by running the processes found in Procfile.dev" 3 | 4 | def call 5 | ::Nox.run("Procfile.dev") 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /src/init.cr: -------------------------------------------------------------------------------- 1 | class LuckyCli::Init 2 | def self.run : Nil 3 | new.run 4 | end 5 | 6 | def run : Nil 7 | LuckyCli::Wizard::Web.run 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /src/lucky_cli.cr: -------------------------------------------------------------------------------- 1 | require "lucky_template" 2 | require "lucky_task" 3 | require "nox" 4 | require "ecr" 5 | require "./lucky_cli/**" 6 | require "./generators/*" 7 | require "./init" 8 | require "./init_custom" 9 | -------------------------------------------------------------------------------- /src/lucky_cli/app_with_sec_tester_template.cr: -------------------------------------------------------------------------------- 1 | class AppWithSecTesterTemplate 2 | getter? generate_auth, browser 3 | 4 | def initialize(@generate_auth : Bool, @browser : Bool) 5 | end 6 | 7 | def render(path : Path) 8 | LuckyTemplate.write!(path, template_folder) 9 | end 10 | 11 | def template_folder 12 | LuckyTemplate.create_folder do |root_dir| 13 | root_dir.add_folder("spec") do |spec_dir| 14 | spec_dir.add_file(Path["flows/security_spec.cr"]) do |io| 15 | ECR.embed("#{__DIR__}/../app_with_sec_tester/spec/flows/security_spec.cr.ecr", io) 16 | end 17 | spec_dir.add_file(Path["setup/sec_tester.cr"]) do |io| 18 | ECR.embed("#{__DIR__}/../app_with_sec_tester/spec/setup/sec_tester.cr.ecr", io) 19 | end 20 | end 21 | end 22 | end 23 | end 24 | -------------------------------------------------------------------------------- /src/lucky_cli/version.cr: -------------------------------------------------------------------------------- 1 | module LuckyCli 2 | VERSION = {{ `shards version #{__DIR__}`.chomp.stringify }} 3 | end 4 | -------------------------------------------------------------------------------- /src/lucky_cli/wizard/labeled_yes_no_question.cr: -------------------------------------------------------------------------------- 1 | class LuckyCli::Wizard::LabeledYesNoQuestion 2 | getter question_text, yes_label, no_label 3 | 4 | def initialize( 5 | @question_text : String, 6 | @yes_label : String, 7 | @no_label : String, 8 | ) 9 | end 10 | 11 | def self.ask(*args, **named_args) : Bool 12 | new(*args, **named_args).ask 13 | end 14 | 15 | def ask : Bool 16 | print question_text.colorize.bold.to_s + 17 | " (#{yes_label}/#{no_label}): ".colorize.green.bold.to_s 18 | answer = gets.try(&.strip.gsub("'", "").downcase) 19 | case answer 20 | when yes_label 21 | true 22 | when no_label 23 | false 24 | else 25 | puts "\nMust be '#{yes_label}' or '#{no_label}'".colorize.red 26 | ask 27 | end 28 | end 29 | end 30 | -------------------------------------------------------------------------------- /src/lucky_cli/wizard/project_name_question.cr: -------------------------------------------------------------------------------- 1 | class LuckyCli::ProjectNameQuestion 2 | def ask : String 3 | name = ProjectName.new(prompt_for_project_name) 4 | 5 | if name.valid? 6 | name.to_s 7 | else 8 | puts "\n" 9 | puts name.validation_error_message.colorize.red 10 | ask 11 | end 12 | end 13 | 14 | private def prompt_for_project_name 15 | print "#{"Project name?".colorize.bold}: " 16 | 17 | gets.try(&.strip.gsub("'", "")) || "" 18 | end 19 | end 20 | -------------------------------------------------------------------------------- /src/lucky_cli/wizard/yes_no_question.cr: -------------------------------------------------------------------------------- 1 | class LuckyCli::Wizard::YesNoQuestion 2 | def self.ask(question_text : String) : Bool 3 | LabeledYesNoQuestion.ask(question_text, yes_label: "y", no_label: "n") 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /src/web_app_skeleton/.crystal-version.ecr: -------------------------------------------------------------------------------- 1 | <%= crystal_version %> 2 | -------------------------------------------------------------------------------- /src/web_app_skeleton/.env.ecr: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luckyframework/lucky_cli/ccaa571765582086d437efe0da4f439a19929d64/src/web_app_skeleton/.env.ecr -------------------------------------------------------------------------------- /src/web_app_skeleton/Procfile.dev.ecr: -------------------------------------------------------------------------------- 1 | system_check: crystal script/system_check.cr 2 | <%- if api_only? -%> 3 | web: lucky watch 4 | <%- else -%> 5 | web: lucky watch --reload-browser 6 | assets: yarn watch 7 | <%- end -%> 8 | -------------------------------------------------------------------------------- /src/web_app_skeleton/Procfile.ecr: -------------------------------------------------------------------------------- 1 | web: bin/app 2 | release: lucky db.migrate 3 | -------------------------------------------------------------------------------- /src/web_app_skeleton/config/application.cr.ecr: -------------------------------------------------------------------------------- 1 | # This file may be used for custom Application configurations. 2 | # It will be loaded before other config files. 3 | # 4 | # Read more on configuration: 5 | # https://luckyframework.org/guides/getting-started/configuration#configuring-your-own-code 6 | 7 | # Use this code as an example: 8 | # 9 | # ``` 10 | # module Application 11 | # Habitat.create do 12 | # setting support_email : String 13 | # setting lock_with_basic_auth : Bool 14 | # end 15 | # end 16 | # 17 | # Application.configure do |settings| 18 | # settings.support_email = "support@myapp.io" 19 | # settings.lock_with_basic_auth = LuckyEnv.staging? 20 | # end 21 | # 22 | # # In your application, call 23 | # # `Application.settings.support_email` anywhere you need it. 24 | # ``` 25 | -------------------------------------------------------------------------------- /src/web_app_skeleton/config/colors.cr.ecr: -------------------------------------------------------------------------------- 1 | # This enables the color output when in development or test 2 | # Check out the Colorize docs for more information 3 | # https://crystal-lang.org/api/Colorize.html 4 | Colorize.enabled = LuckyEnv.development? || LuckyEnv.test? 5 | -------------------------------------------------------------------------------- /src/web_app_skeleton/config/cookies.cr.ecr: -------------------------------------------------------------------------------- 1 | require "./server" 2 | 3 | Lucky::Session.configure do |settings| 4 | settings.key = "_<%= crystal_project_name %>_session" 5 | end 6 | 7 | Lucky::CookieJar.configure do |settings| 8 | settings.on_set = ->(cookie : HTTP::Cookie) { 9 | # If ForceSSLHandler is enabled, only send cookies over HTTPS 10 | cookie.secure(Lucky::ForceSSLHandler.settings.enabled) 11 | 12 | # By default, don't allow reading cookies with JavaScript 13 | cookie.http_only(true) 14 | 15 | # Restrict cookies to a first-party or same-site context 16 | cookie.samesite(:lax) 17 | 18 | # Set all cookies to the root path by default 19 | cookie.path("/") 20 | 21 | # You can set other defaults for cookies here. For example: 22 | # 23 | # cookie.expires(1.year.from_now).domain("mydomain.com") 24 | } 25 | end 26 | -------------------------------------------------------------------------------- /src/web_app_skeleton/config/env.cr.ecr: -------------------------------------------------------------------------------- 1 | # Environments are managed using `LuckyEnv`. By default, development, production 2 | # and test are supported. See 3 | # https://luckyframework.org/guides/getting-started/configuration for details. 4 | # 5 | # The default environment is development unless the environment variable 6 | # LUCKY_ENV is set. 7 | # 8 | # Example: 9 | # ``` 10 | # LuckyEnv.environment # => "development" 11 | # LuckyEnv.development? # => true 12 | # LuckyEnv.production? # => false 13 | # LuckyEnv.test? # => false 14 | # ``` 15 | # 16 | # New environments can be added using the `LuckyEnv.add_env` macro. 17 | # 18 | # Example: 19 | # ``` 20 | # LuckyEnv.add_env :staging 21 | # LuckyEnv.staging? # => false 22 | # ``` 23 | # 24 | # To determine whether or not a `LuckyTask` is currently running, you can use 25 | # the `LuckyEnv.task?` predicate. 26 | # 27 | # Example: 28 | # ``` 29 | # LuckyEnv.task? # => false 30 | # ``` 31 | 32 | # Add a staging environment. 33 | # LuckyEnv.add_env :staging 34 | -------------------------------------------------------------------------------- /src/web_app_skeleton/config/error_handler.cr.ecr: -------------------------------------------------------------------------------- 1 | Lucky::ErrorHandler.configure do |settings| 2 | settings.show_debug_output = !LuckyEnv.production? 3 | end 4 | -------------------------------------------------------------------------------- /src/web_app_skeleton/config/route_helper.cr.ecr: -------------------------------------------------------------------------------- 1 | # This is used when generating URLs for your application 2 | Lucky::RouteHelper.configure do |settings| 3 | if LuckyEnv.production? 4 | # Example: https://my_app.com 5 | settings.base_uri = ENV.fetch("APP_DOMAIN") 6 | else 7 | # Set domain to the default host/port in development/test 8 | settings.base_uri = "http://localhost:#{Lucky::ServerSettings.port}" 9 | end 10 | end 11 | -------------------------------------------------------------------------------- /src/web_app_skeleton/config/watch.yml.ecr: -------------------------------------------------------------------------------- 1 | host: 127.0.0.1 2 | port: 3000 3 | reload_port: 3001 4 | -------------------------------------------------------------------------------- /src/web_app_skeleton/db/migrations/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luckyframework/lucky_cli/ccaa571765582086d437efe0da4f439a19929d64/src/web_app_skeleton/db/migrations/.keep -------------------------------------------------------------------------------- /src/web_app_skeleton/script/helpers/function_helpers.cr.ecr: -------------------------------------------------------------------------------- 1 | require "colorize" 2 | 3 | # These are helper methods provided to help keep your code 4 | # clean. Add new methods, or alter these as needed. 5 | 6 | def notice(message : String) : Nil 7 | puts "\n▸ #{message}" 8 | end 9 | 10 | def print_done : Nil 11 | puts "✔ Done" 12 | end 13 | 14 | def print_error(message : String) : Nil 15 | puts "There is a problem with your system setup:\n".colorize.red.bold 16 | puts "#{message}\n".colorize.red.bold 17 | Process.exit(1) 18 | end 19 | 20 | def command_not_found(command : String) : Bool 21 | Process.find_executable(command).nil? 22 | end 23 | 24 | def command_not_running(command : String, *args) : Bool 25 | output = IO::Memory.new 26 | code = Process.run(command, args, output: output).exit_code 27 | code > 0 28 | end 29 | 30 | def run_command(command : String, *args) : Nil 31 | Process.run(command, args, output: STDOUT, error: STDERR, input: STDIN) 32 | end 33 | -------------------------------------------------------------------------------- /src/web_app_skeleton/script/setup.cr.ecr: -------------------------------------------------------------------------------- 1 | require "./helpers/*" 2 | 3 | notice "Running System Check" 4 | 5 | require "./system_check" 6 | 7 | print_done 8 | 9 | <%- if browser? -%> 10 | notice "Installing node dependencies" 11 | run_command "yarn", "install", "--no-progress" 12 | 13 | notice "Compiling assets" 14 | run_command "yarn", "dev" 15 | 16 | print_done 17 | <%- end -%> 18 | 19 | notice "Installing shards" 20 | run_command "shards", "install" 21 | 22 | if !File.exists?(".env") 23 | notice "No .env found. Creating one." 24 | File.touch ".env" 25 | print_done 26 | end 27 | 28 | notice "Setting up the database" 29 | 30 | run_command "lucky", "db.setup" 31 | 32 | notice "Seeding the database with required and sample records" 33 | run_command "lucky", "db.seed.required_data" 34 | run_command "lucky", "db.seed.sample_data" 35 | 36 | print_done 37 | notice "Run 'lucky dev' to start the app" 38 | -------------------------------------------------------------------------------- /src/web_app_skeleton/script/system_check.cr.ecr: -------------------------------------------------------------------------------- 1 | require "./helpers/*" 2 | 3 | # Use this script to check the system for required tools and process that your app needs. 4 | # A few helper functions are provided to keep the code simple. See the 5 | # script/helpers/function_helpers.cr file for more examples. 6 | # 7 | # A few examples you might use here: 8 | # * 'lucky db.verify_connection' to test postgres can be connected 9 | # * Checking that elasticsearch, redis, or postgres is installed and/or booted 10 | # * Note: Booting additional processes for things like mail, background jobs, etc... 11 | # should go in your Procfile.dev. 12 | 13 | <%- if browser? -%> 14 | if command_not_found "yarn" 15 | print_error "Yarn is not installed\n See https://yarnpkg.com/lang/en/docs/install/ for install instructions." 16 | end 17 | <%- end -%> 18 | 19 | # CUSTOM PRE-BOOT CHECKS 20 | # example: 21 | # if command_not_running "redis-cli", "ping" 22 | # print_error "Redis is not running." 23 | # end 24 | -------------------------------------------------------------------------------- /src/web_app_skeleton/spec/setup/clean_database.cr.ecr: -------------------------------------------------------------------------------- 1 | Spec.before_each do 2 | AppDatabase.truncate 3 | end 4 | -------------------------------------------------------------------------------- /src/web_app_skeleton/spec/setup/reset_emails.cr.ecr: -------------------------------------------------------------------------------- 1 | Spec.before_each do 2 | Carbon::DevAdapter.reset 3 | end 4 | -------------------------------------------------------------------------------- /src/web_app_skeleton/spec/setup/setup_database.cr.ecr: -------------------------------------------------------------------------------- 1 | Db::Create.new(quiet: true).call 2 | Db::Migrate.new(quiet: true).call 3 | -------------------------------------------------------------------------------- /src/web_app_skeleton/spec/setup/start_app_server.cr.ecr: -------------------------------------------------------------------------------- 1 | app_server = AppServer.new 2 | 3 | spawn do 4 | app_server.listen 5 | end 6 | 7 | Spec.after_suite do 8 | <%- if browser? -%> 9 | LuckyFlow.shutdown 10 | <%- end -%> 11 | app_server.close 12 | end 13 | -------------------------------------------------------------------------------- /src/web_app_skeleton/spec/support/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luckyframework/lucky_cli/ccaa571765582086d437efe0da4f439a19929d64/src/web_app_skeleton/spec/support/.keep -------------------------------------------------------------------------------- /src/web_app_skeleton/spec/support/api_client.cr.ecr: -------------------------------------------------------------------------------- 1 | class ApiClient < Lucky::BaseHTTPClient 2 | app AppServer.new 3 | 4 | def initialize 5 | super 6 | headers("Content-Type": "application/json") 7 | end 8 | <%- if generate_auth? -%> 9 | 10 | def self.auth(user : User) 11 | new.headers("Authorization": UserToken.generate(user)) 12 | end 13 | <%- end -%> 14 | end 15 | -------------------------------------------------------------------------------- /src/web_app_skeleton/spec/support/factories/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luckyframework/lucky_cli/ccaa571765582086d437efe0da4f439a19929d64/src/web_app_skeleton/spec/support/factories/.keep -------------------------------------------------------------------------------- /src/web_app_skeleton/src/actions/api_action.cr.ecr: -------------------------------------------------------------------------------- 1 | # Include modules and add methods that are for all API requests 2 | abstract class ApiAction < Lucky::Action 3 | # APIs typically do not need to send cookie/session data. 4 | # Remove this line if you want to send cookies in the response header. 5 | disable_cookies 6 | accepted_formats [:json] 7 | <%- if generate_auth? -%> 8 | 9 | include Api::Auth::Helpers 10 | 11 | # By default all actions require sign in. 12 | # Add 'include Api::Auth::SkipRequireAuthToken' to your actions to allow all requests. 13 | include Api::Auth::RequireAuthToken 14 | <%- end -%> 15 | 16 | # By default all actions are required to use underscores to separate words. 17 | # Add 'include Lucky::SkipRouteStyleCheck' to your actions if you wish to ignore this check for specific routes. 18 | include Lucky::EnforceUnderscoredRoute 19 | end 20 | -------------------------------------------------------------------------------- /src/web_app_skeleton/src/actions/home/index.cr.ecr: -------------------------------------------------------------------------------- 1 | <%- if api_only? -%> 2 | class Home::Index < ApiAction 3 | <%- if generate_auth? -%> 4 | include Api::Auth::SkipRequireAuthToken 5 | 6 | <%- end -%> 7 | get "/" do 8 | json({hello: "Hello World from Home::Index"}) 9 | end 10 | end 11 | <%- else -%> 12 | class Home::Index < BrowserAction 13 | <%- if generate_auth? -%> 14 | include Auth::AllowGuests 15 | 16 | get "/" do 17 | if current_user? 18 | redirect Me::Show 19 | else 20 | # When you're ready change this line to: 21 | # 22 | # redirect SignIns::New 23 | # 24 | # Or maybe show signed out users a marketing page: 25 | # 26 | # html Marketing::IndexPage 27 | html Lucky::WelcomePage 28 | end 29 | end 30 | <%- else -%> 31 | get "/" do 32 | html Lucky::WelcomePage 33 | end 34 | <%- end -%> 35 | end 36 | <%- end -%> 37 | -------------------------------------------------------------------------------- /src/web_app_skeleton/src/actions/mixins/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luckyframework/lucky_cli/ccaa571765582086d437efe0da4f439a19929d64/src/web_app_skeleton/src/actions/mixins/.keep -------------------------------------------------------------------------------- /src/web_app_skeleton/src/app.cr.ecr: -------------------------------------------------------------------------------- 1 | require "./shards" 2 | 3 | <%- if browser? -%> 4 | # Load the asset manifest 5 | Lucky::AssetHelpers.load_manifest "public/mix-manifest.json" 6 | 7 | <%- end -%> 8 | require "../config/server" 9 | require "./app_database" 10 | require "../config/**" 11 | require "./models/base_model" 12 | require "./models/mixins/**" 13 | require "./models/**" 14 | require "./queries/mixins/**" 15 | require "./queries/**" 16 | require "./operations/mixins/**" 17 | require "./operations/**" 18 | require "./serializers/base_serializer" 19 | require "./serializers/**" 20 | require "./emails/base_email" 21 | require "./emails/**" 22 | require "./actions/mixins/**" 23 | require "./actions/**" 24 | <%- if browser? -%> 25 | require "./components/base_component" 26 | require "./components/**" 27 | require "./pages/**" 28 | <%- end -%> 29 | require "../db/migrations/**" 30 | require "./app_server" 31 | -------------------------------------------------------------------------------- /src/web_app_skeleton/src/app_database.cr.ecr: -------------------------------------------------------------------------------- 1 | class AppDatabase < Avram::Database 2 | end 3 | -------------------------------------------------------------------------------- /src/web_app_skeleton/src/emails/base_email.cr.ecr: -------------------------------------------------------------------------------- 1 | # Learn about sending emails 2 | # https://luckyframework.org/guides/emails/sending-emails-with-carbon 3 | abstract class BaseEmail < Carbon::Email 4 | # You can add defaults using the 'inherited' hook 5 | # 6 | # Example: 7 | # 8 | # macro inherited 9 | # from default_from 10 | # end 11 | # 12 | # def default_from 13 | # Carbon::Address.new("support@app.com") 14 | # end 15 | end 16 | -------------------------------------------------------------------------------- /src/web_app_skeleton/src/models/base_model.cr.ecr: -------------------------------------------------------------------------------- 1 | abstract class BaseModel < Avram::Model 2 | def self.database : Avram::Database.class 3 | AppDatabase 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /src/web_app_skeleton/src/models/mixins/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luckyframework/lucky_cli/ccaa571765582086d437efe0da4f439a19929d64/src/web_app_skeleton/src/models/mixins/.keep -------------------------------------------------------------------------------- /src/web_app_skeleton/src/operations/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luckyframework/lucky_cli/ccaa571765582086d437efe0da4f439a19929d64/src/web_app_skeleton/src/operations/.keep -------------------------------------------------------------------------------- /src/web_app_skeleton/src/operations/mixins/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luckyframework/lucky_cli/ccaa571765582086d437efe0da4f439a19929d64/src/web_app_skeleton/src/operations/mixins/.keep -------------------------------------------------------------------------------- /src/web_app_skeleton/src/project_name.cr.ecr: -------------------------------------------------------------------------------- 1 | # Typically you will not use or modify this file. 'shards build' and some 2 | # other crystal tools will sometimes use this. 3 | # 4 | # When this file is compiled/run it will require and run 'start_server', 5 | # which as its name implies will start the server for you app. 6 | require "./start_server" 7 | -------------------------------------------------------------------------------- /src/web_app_skeleton/src/queries/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luckyframework/lucky_cli/ccaa571765582086d437efe0da4f439a19929d64/src/web_app_skeleton/src/queries/.keep -------------------------------------------------------------------------------- /src/web_app_skeleton/src/queries/mixins/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luckyframework/lucky_cli/ccaa571765582086d437efe0da4f439a19929d64/src/web_app_skeleton/src/queries/mixins/.keep -------------------------------------------------------------------------------- /src/web_app_skeleton/src/serializers/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luckyframework/lucky_cli/ccaa571765582086d437efe0da4f439a19929d64/src/web_app_skeleton/src/serializers/.keep -------------------------------------------------------------------------------- /src/web_app_skeleton/src/serializers/base_serializer.cr.ecr: -------------------------------------------------------------------------------- 1 | abstract class BaseSerializer 2 | include Lucky::Serializable 3 | 4 | def self.for_collection(collection : Enumerable, *args, **named_args) : Array(self) 5 | collection.map do |object| 6 | new(object, *args, **named_args) 7 | end 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /src/web_app_skeleton/src/serializers/error_serializer.cr.ecr: -------------------------------------------------------------------------------- 1 | # This is the default error serializer generated by Lucky. 2 | # Feel free to customize it in any way you like. 3 | class ErrorSerializer < BaseSerializer 4 | def initialize( 5 | @message : String, 6 | @details : String? = nil, 7 | @param : String? = nil, # so you can track which param (if any) caused the problem 8 | ) 9 | end 10 | 11 | def render 12 | {message: @message, param: @param, details: @details} 13 | end 14 | end 15 | -------------------------------------------------------------------------------- /src/web_app_skeleton/src/shards.cr.ecr: -------------------------------------------------------------------------------- 1 | # Load .env file before any other config or app code 2 | require "lucky_env" 3 | LuckyEnv.load?(".env") 4 | 5 | # Require your shards here 6 | require "lucky" 7 | require "avram/lucky" 8 | require "carbon" 9 | <%- if generate_auth? -%> 10 | require "authentic" 11 | require "jwt" 12 | <%- end -%> 13 | -------------------------------------------------------------------------------- /src/web_app_skeleton/src/start_server.cr.ecr: -------------------------------------------------------------------------------- 1 | require "./app" 2 | 3 | Habitat.raise_if_missing_settings! 4 | 5 | if LuckyEnv.development? 6 | Avram::Migrator::Runner.new.ensure_migrated! 7 | Avram::SchemaEnforcer.ensure_correct_column_mappings! 8 | end 9 | 10 | app_server = AppServer.new 11 | <%- if !proxied_through_browsersync? -%> 12 | puts "Listening on http://#{app_server.host}:#{app_server.port}" 13 | <%- end -%> 14 | 15 | Signal::INT.trap do 16 | app_server.close 17 | end 18 | 19 | app_server.listen 20 | -------------------------------------------------------------------------------- /src/web_app_skeleton/tasks.cr.ecr: -------------------------------------------------------------------------------- 1 | # This file loads your app and all your tasks when running 'lucky' 2 | # 3 | # Run 'lucky --help' to see all available tasks. 4 | # 5 | # Learn to create your own tasks: 6 | # https://luckyframework.org/guides/command-line-tasks/custom-tasks 7 | 8 | # See `LuckyEnv#task?` 9 | ENV["LUCKY_TASK"] = "true" 10 | 11 | # Load Lucky and the app (actions, models, etc.) 12 | require "./src/app" 13 | require "lucky_task" 14 | 15 | # You can add your own tasks here in the ./tasks folder 16 | require "./tasks/**" 17 | 18 | # Load migrations 19 | require "./db/migrations/**" 20 | 21 | # Load Lucky tasks (dev, routes, etc.) 22 | require "lucky/tasks/**" 23 | require "avram/lucky/tasks" 24 | 25 | LuckyTask::Runner.run 26 | -------------------------------------------------------------------------------- /src/web_app_skeleton/tasks/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luckyframework/lucky_cli/ccaa571765582086d437efe0da4f439a19929d64/src/web_app_skeleton/tasks/.keep --------------------------------------------------------------------------------