{"id":15725893,"url":"https://github.com/ndrean/godwd","last_synced_at":"2026-04-17T15:32:24.805Z","repository":{"id":124424842,"uuid":"289969121","full_name":"ndrean/godwd","owner":"ndrean","description":"Rails back-end of https://thedownwinder.com","archived":false,"fork":false,"pushed_at":"2020-10-27T23:39:13.000Z","size":799,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-02-06T06:46:20.071Z","etag":null,"topics":["cloudinary","jwt","knock","nginx","puma","rails","redis","sidekiq","sse"],"latest_commit_sha":null,"homepage":"","language":"Ruby","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/ndrean.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2020-08-24T15:40:44.000Z","updated_at":"2020-12-06T01:09:39.000Z","dependencies_parsed_at":"2023-08-07T16:34:51.181Z","dependency_job_id":null,"html_url":"https://github.com/ndrean/godwd","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ndrean%2Fgodwd","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ndrean%2Fgodwd/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ndrean%2Fgodwd/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ndrean%2Fgodwd/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ndrean","download_url":"https://codeload.github.com/ndrean/godwd/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":246402584,"owners_count":20771342,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["cloudinary","jwt","knock","nginx","puma","rails","redis","sidekiq","sse"],"created_at":"2024-10-03T22:24:51.187Z","updated_at":"2026-04-17T15:32:19.773Z","avatar_url":"https://github.com/ndrean.png","language":"Ruby","funding_links":[],"categories":[],"sub_categories":[],"readme":"# The app:\n\nA Rails API live broadcasting changes in a PSQL db, sending background mails.\n\n- back-end code is written with `Ruby-On-Rails`\n\n  ![Ruby-On_Rails](https://github.com/ndrean/godwd/blob/master/public/Rails.png)\n\n- and uses `Puma` as concurrent webserver, and reverse-proxied with `nginx`\n\n  ![nginx](https://github.com/ndrean/godwd/blob/master/public/nginx.png)\n\nso that we have the following schema:\n\n![Nginx Puma Rack Rails](https://github.com/ndrean/godwd/blob/master/public/Nginx-puma-rack-rails.png)\n\nThe app is served from Heroku (free dyno...)\n\n![Heroku](https://github.com/ndrean/godwd/blob/master/public/Heroku.png)\n\n- uses a `PostgreSQL` database\n\n  ![Postgres](https://github.com/ndrean/godwd/blob/master/public/Postgres.png)\n\n  - changes in the database are live streamed with **Server-Sent-Events**\n\n- uses `Sidekiq` with `Redis` as the ActiveJob adapter for mailing\n\n  ![Sidekiq](https://github.com/ndrean/godwd/blob/master/public/sidekiq.png)\n  ![Redis](https://github.com/ndrean/godwd/blob/master/public/Redis.png)\n\n- `Mailgun` for the mialing service\n\n  ![Mailgun](https://github.com/ndrean/godwd/blob/master/public/mailgun.png)\n\n- `Cloudinary` (without ActiveStorage) for storing images. The upload is done directly to Cloudinary by the front end. The front end sends the url of the image, and the back end saves it. The back end only deletes the image async with a Sidekiq worker.\n\n  ![Cloudinary](https://github.com/ndrean/godwd/blob/master/public/Cloudinary.png)\n\n- the data sent from the server can be gzip or brotli compressed. Here we chosed to let nginx take care of this.\n\n- The authentification uses the gem **Knock** (with BCrypt and JWT).\n\nThe front end is:\n\n- a `React` fromt end (using Create-React-app)\n\n  ![React](https://github.com/ndrean/godwd/blob/master/public/React.png)\n\n- uses a `Facebook Login` component\n\n  ![FBLogin](https://github.com/ndrean/godwd/blob/master/public/FB-Login.png)\n\n- uploads images directly to `Cloudinary`\n\n  ![ReactCloudinary](https://github.com/ndrean/godwd/blob/master/public/reactcloudinary.png)\n\n- displays mapq with `Leaflet.js` and the `arcGis` service for reverse geolocation\n\n  ![Leaflet.js](https://github.com/ndrean/godwd/blob/master/public/leafletjs.png)\n\n  ![arcGis](https://github.com/ndrean/godwd/blob/master/public/arcGis.png)\n\nThe domain-registrar has been set with AWS Route53, and the static front end files are hosted in a AWS S3 bucket: create a bucket, upload code, make it public, set public access policy, configure it as web hosting, set DNS CNAME\n\n![AWS-S3](https://github.com/ndrean/godwd/blob/master/public/AWS-S3.png)\n\nand we use a CDN: Cloudfare that provides the SSL certificates. To make the Cloudfare subdomain work with S3, you add the domain to Cloudfare, and the domain-registrar DNS servers for the Cloudflare, and DNS records accordingly. Then we have:\n\n![Cloudfare](https://github.com/ndrean/godwd/blob/master/public/Cloudfare.png)\n\n# Database structure\n\n3 tables, where 'events' is a joint table.\n\n- The field `events.participants` has a format Postgres of `jsonb`, an array of type `{email: 'toto@test.com', notif:\"false\", ptoken:\"wmkm234kxkl\"}`\n\n- `end_gps` and `start_gps` are arrays of 2 decimals, `[45.23424,1.234234]`\n\n- a user has the field `password_digest` even if we use the field `password`: the gem `bcrypt` saves it encrypted (the key is the Rails `secret_bse_key`).\n- the fields `uid` and `access-token` are copies of a users's Facebook credentials.\n- the `confirm_token` is used on 'sign up': the `Knock` gem generates a token that is saved in the db, and sent in a link by email in the user. when the confirms, the db reads this token and confirms the user.\n\n![Database schema](https://github.com/ndrean/godwd/blob/master/public/goDownWind.png)\n\n```\nCREATE TABLE \"events\" (\n  \"id\" varchar,\n  \"directCLurl\" string,\n  \"publicID\" string,\n  \"url\" string,\n  \"participants\" jsonb,\n  \"user_id\" bigint,\n  \"itinary_id\" bigint,\n  \"created_at\" datetime,\n  \"updated_at\" datetime,\n  \"comment\" text\n);\n\nCREATE TABLE \"itinaries\" (\n  \"id\" varchar,\n  \"date\" date,\n  \"start\" string,\n  \"end\" string,\n  \"distance\" decimal,\n  \"created_at\" datetime,\n  \"updated_at\" datetime,\n  \"end_gps\" decimal,\n  \"start_gps\" decimal\n);\n\nCREATE TABLE \"users\" (\n  \"id\" varchar,\n  \"email\" string,\n  \"password_digest\" string,\n  \"confirm_token\" string,\n  \"confirm_email\" boolean,\n  \"access_token\" string,\n  \"uid\" string,\n  \"created_at\" datetime,\n  \"updated_at\" datetime\n);\n\nALTER TABLE \"itinaries\" ADD CONSTRAINT \"fk_rails_events_itinaries\" FOREIGN KEY (\"id\") REFERENCES \"events\" (\"itinary_id\");\n\nALTER TABLE \"users\" ADD CONSTRAINT \"fk_rails_events_users\" FOREIGN KEY (\"id\") REFERENCES \"events\" (\"user_id\");\n```\n\n# schema.rb\n\n\u003chttps://edgeguides.rubyonrails.org/active_record_migrations.html#schema-dumping-and-you\u003e\n\n```ruby\n# /config/application.rb\nconfig.active_record.schema_format :ruby # :sql\n```\n\nso we can do `rails db:schema.load` instead of running all the migrations with `rails db:migrate`.\n\nOnce `docker-compose up`, we can do:\n\n```bash\ndocker-compose exec rails db:create\ndocker-compose exec web rails db:schema:load\ndocker-compose exec web rails db:seed\n```\n\n# TODO :\n\n- Test implement Request Rate Limiter ? (throttling on login? on 'new event')\n\n\u003e gem `rack-attack`\n\n- try SSE with Redis publis/subscribe...(can't make it work...)\n\n- try SSE with Postgres LISTEN/NOTIFIY ?? =\u003e capture the delete action ?\n\n# HTTP Caching w/Rails\n\n\u003e `api:rails: ConditionalGet`\n\u003e This is a Rails API so only `if stale` is possible. -`if stale?` renders 'Completed 304 Not Modified in 33ms' or queries again when necessary.\n\nRead:\n\u003chttps://engineering.shopify.com/blogs/engineering/write-fast-code-ruby-rails\u003e\n\n\u003chttps://thoughtbot.com/blog/take-control-of-your-http-caching-in-rails\u003e\n\n\u003chttps://www.synbioz.com/blog/tech/du-cache-http-avec-les-etag-en-rails\u003e\n\u003chttps://blog.bigbinary.com/2016/03/08/rails-5-switches-from-strong-etags-to-weak-tags.html?utm_source=rubyweekly\u0026utm_medium=email\u003e\n\nOther HTTP caching with Rails (non API):\n\n- if request is `fresh_when(@variable)` Etag will render 304 Not modified response\n\n- set HTTP Cache-Control header: `expires_in 2.hours, public: true`\n  \u003chttps://api.rubyonrails.org/classes/ActionController/ConditionalGet.html#method-i-expires_in\u003e\n\n  \u003chttps://devcenter.heroku.com/articles/http-caching-ruby-rails#conditional-cache-headers\u003e\n\n# Note: VPS for Rails\n\nTo be tested.\n\n\u003chttps://mydigital-life.online/comment-installer-rails-sur-un-vps/\u003e\n\n# Async jobs:\n\n- `ActiveJob`. Set `config.active_job.queue_adapter = :sidekiq` in `/config/environments/dev-prod.rb`, and use `perform_later` or `deliver_later`. We alos need to declare a class inheriting from `ApplicationJob`and defined `queure_as :mailer` for example. \u003chttps://github.com/mperham/sidekiq/wiki/Active+Job\u003e\n\n- or directly `Sidekiq worker`: example with RemoveDirectLink. Create a worker under `/app/workers/my_worker.rb` with `include Sidekiq::Worker` and use `perform_async` in the controller).\n\n## Sidekiq setup\n\n- added to '/config/application;rb`the declaration:`config.active_job.queue_adapter = :sidekiq` tells ActiveJob to use Sidekiq.\n\n- Added `/config/sidekiq.rb` with `Redis`.\n\n\u003chttps://github.com/mperham/sidekiq/wiki\u003e\n\u003chttps://enmanuelmedina.com/en/posts/rails-sidekiq-heroku\u003e\n\nWhen we defined the route:\n\n```ruby\nmount Sidekiq::Web =\u003e '/sidekiq'\n```\n\nthen the sidekiq console is available at http://localhost:3001/sidekiq.\n\nTo run Sidekiq, we do:\n\n```bash\nbundle exec sidekiq --environment development -C config/sidekiq.yml\n```\n\nThis will be a separate process for the process launcher `Foreman`:\n\n```bash\nworker: bundle exec sidekiq -C ./config/sidekiq.yml\n```\n\n## Mail background jobs\n\n- Note: gem 'mailgun-ruby` is usefull to get the info that a mail has been sent.\n  \u003chttps://github.com/mailgun/mailgun-ruby\u003e\n\nWe declare in '/config/application.rb' (for all environments):\n`config.action_mailer.delivery_method = :smtp`\n\nWe don't use ActiveJob here to send async a mail, we use ActionMailer with Sidekiq and the method `deliver_later` \u003chttps://github.com/mperham/sidekiq/wiki/Active-Job\u003e. We define a class (`EventMailer` and `UserMailer`, both inheriting from `ApplicationMailer`) with actions that will be used by the controller. Each method uses a `html.erb` view to be delivered via the mail protocole `smtp`. The views use the instance variables defined in the actions.\n\nThe mails are queued in a queue named `mailers` and Sidekiq uses a Redis db.\n\nThe usage of Redis is declared in the '/app/config/initializers/sidekiq.rb' and the gem 'redis'.\n\nFor Heroku, we need to set the config vars `REDIS_PROVIDER` and `REDIS_URL`.\n\nFor 'locahost', we set `REDIS_URL='redis://localhost:6379'`.\n\nWe use `Mailgun`. Once we have registered our domain, we set the DNS TXT \u0026 CNAME provided by Mailgun in the registar provider (OVH or AWS), and the SMTP data in `/config/initializers/smtp.rb`:\n\n```ruby\nActionMailer::Base.smtp_settings = {\n  address: 'smtp.mailgun.org',\n  port: 587,\n  domain: ENV['DOMAIN_NAME'], \u003c=\u003e \"thedownwinder.com\"\n  user_name: ENV['SMTP_USER_NAME'], \u003c=\u003e \"postmaster@thedownwinder.com\"\n  password: ENV['MAIL_APP_PASSWORD'], \u003c=\u003e \"eac87f019exxxx\"\n  authentication: :plain,\n  enable_starttls_auto: true\n}\n```\n\n# Cloudinary remove with Sidekiq\n\n- gem `Cloudinary`\n  \u003chttps://cloudinary.com/documentation/rails_integration#rails_getting_started_guide\u003e\n\n\u003chttps://github.com/cloudinary/cloudinary_gem\u003e\n\n\u003e credentials: they are passed manually to each call in the method, and added as `config vars` to Heroku. The `/config/cloudinary.yml` is not used since it doesn't accept `.env` variables.\n\nWe use the worker `RemoveDirectLink` to async remove a picture from Cloudinary by the Rails backend. We can use activeJob or directly a worker. The '/workers' folder is not read by Rails, only Sidekiq, declared\n\nHere, we used a worker (without ActiveJob and `default queue`, just including `Sidekiq::Worker`) and use `perform_async`.\n\n```ruby\n # /App/workers/remove_direct_link.rb\nclass RemoveDirectLink\n  include Sidekiq::Worker\n\n  def perform(event_publicID)\n    auth = {\n        cloud_name: Rails.application.credentials.CL[:CLOUD_NAME],\n        api_key: Rails.application.credentials.CL[:API_KEY],\n        api_secret: Rails.application.credentials.CL[:API_SECRET]\n      }\n    return if !event_publicID\n    Cloudinary::Uploader.destroy(event_publicID, auth)\n  end\nend\n```\n\nWe could also use ActiveJob (cf mails) by defining a class inheriting from `ApplicationJob` and specifying the 'queue' and use `deliver_later`. Here, we use the Cloudinary method `destroy`:\n\n\u003chttps://cloudinary.com/documentation/image_upload_api_reference#destroy_method\u003e\n\n```\n# /app/jobs/remove_direct_link.rb\nclass RemoveDirectLink \u003c ApplicationJob\n  queue_as :default\n\n  def perform(event_publicID)\n    auth = {\n        cloud_name: ENV['CL_CLOUD_NAME'],\n        api_key: ENV['CL_API_KEY'],\n        api_secret: ENV['CL_API_SECRET']\n      }\n\n    return if !event_publicID\n    Cloudinary::Uploader.destroy(event_publicID, auth)\n  end\nend\n```\n\n# Puma port setup\n\nReact will run on '3000' and Rails will run on port '3001'\n\n```ruby\n# /config/puma.rb\nport        ENV.fetch(\"PORT\") { 3001 }\n```\n\n# Bootsnap issue\n\n\u003chttps://github.com/Shopify/bootsnap/issues/262\u003e\nRemoved line 60 ` # config.file_watcher = ActiveSupport::EventedFileUpdateChecker`in '/config/development.rb' which uses `listen`.\n\n# CORS\n\nCORS stands for Cross-Origin Resource Sharing, a standard that lets developers specify who can access the assets on a server and what HTTP requests are accepted. For example, a restrictive 'same-origin' policy would prevent your Rails API at localhost:3001 from sending and receiving data to your front-end at localhost:3000.\n\n```ruby\n# /config/application.rb\nconfig.middleware.insert_before 0, Rack::Cors do\n  allow do\n    origins [\"https://thedownwinder.com\", \"http://localhost:3001\", \"http://localhost:8080\"]\n    resource ‘*’,\n      headers: :any,\n      methods: [:get, :post, :options],\n      credentials: true\n  end\nend\n```\n\n# SSE\n\n# Sidekiq, Redis setup\n\n\u003chttps://manuelvanrijn.nl/sidekiq-heroku-redis-calc/\u003e\n\n1 worker, 1 dyno, 5 web thread\n\n```ruby\n# /config/initializers/sidekiq.rb\nif Rails.env.production?\n  Sidekiq.configure_client do |config|\n    config.redis = { url: ENV['REDIS_URL'], size: 3, network_timeout: 5 }\n  end\n\n  Sidekiq.configure_server do |config|\n    config.redis = { url: ENV['REDIS_URL'], size: 5, network_timeout: 5 }\n  end\nend\n```\n\n```ruby\n# .env\nREDIS_URL='redis://localhost:6379'\n\n#/config/initializers/sidekiq.rb\n...config.redis = { url: ENV['REDIS_URL'], size: 2 }\n```\n\nTo run Redis, we do:\n\n```bash\nbrew services redis-server\n```\n\nWe declare another process for Foreman (Procfile):\n\n```bash\nredis: redis-server --port 6379\n```\n\n# Procfile \u0026 Foreman\n\nforeman start -f ProcfileIwant\n\n\u003e Dev localhost mode:\n\n```\napi: bundle exec bin/rails server -p 3001\nworker: bundle exec sidekiq -C ./config/sidekiq.yml\nredis: redis-server --port 6379\n\n```\n\n\u003e Heroku mode:\n\n```\napi: bundle exec bin/rails server -p 3001\nworker: bundle exec sidekiq -C ./config/sidekiq.yml\n```\n\n- settings.config vars:\n\n`REDIS_URL` will be set in 'setttings/config vars' after setting `REDIS_PROVIDER=REDISTOGO_URL` (free)\n\nSet the keys `RAILS_MASTER_KEY` and `SECRET_KEY_BASE` (do `EDITOR=\"code ...wait\" rails credentials:edit` to set)\n\nThe `DATABASE_URL` wil be set by Heroku.\n\n# Compression\n\nWe can use directly gzip or Brotli compression with Rails. For Brotli, use the gem `rack-brotli` and set:\n\n```ruby\n#/config.application.rb\n  config.middleware.use Rack::Deflater\n  config.middleware.use Rack::Brotli\n```\n\n\u003chttps://pawelurbanek.com/rails-gzip-brotli-compression\u003e\n\nSince we use `Nginx`, we will use the inbuild gzip service to we delegate the data compression to nginx.\n\n# Arrays in PostgreSQL\n\n\u003chttps://stackoverflow.com/questions/63404637/rails-submitting-array-to-postgres\u003e\n\nTo accept an array, we need to separate between the ',' when we read the params in the controller.\n\n```ruby\nif params[:event][:itinary_attributes][:start_gps]\n  params[:event][:itinary_attributes][:start_gps] = params[:event][:itinary_attributes][:start_gps][0].split(',')\n  params[:event][:itinary_attributes][:end_gps] = params[:event][:itinary_attributes][:end_gps][0].split(',')\nend\n```\n\nWe can also do the job directly in React: if we read an array `start_gps=[45,1]`, then to pass into `event:{itinary_attributes: {start_gps: [], end_gps: [] } }`, we do:\n\n```js\nfd.append(\"event[itinary_attributes][start_gps][]\", itinary.start_gps[0] || \"\");\nfd.append(\"event[itinary_attributes][start_gps][]\", itinary.start_gps[1] || \"\");\nfd.append(\"event[itinary_attributes][end_gps][]\", itinary.end_gps[0] || \"\");\nfd.append(\"event[itinary_attributes][end_gps][]\", itinary.end_gps[1] || \"\");\n```\n\n# Running the app:\n\nThe Rails api can be run with `rails server` and navigate to `localhost:3001/api/v1/events`.\nYou can run `foreman start -f Procfile_nginx_port.rb` and navigate to `localhost:8000/api/v1/events`: it is reverse-proxied with Nginx.\nYou can run `docker-compose up` and navigate to `localhost:8080/api/v1/events`: the Docker container exposes 8080 \u003e 80 and Rails via 3001:3001.\n\n# Running multiple processes\n\nUse `foreman`\n\nThe `database.yml` musn't use the key `db` (or set `localhost`)\n\n# Docker\n\n\u003chttps://stackoverflow.com/questions/54383233/how-to-dockerize-a-rails-application-with-mysql-nginx-and-cron-tasks\u003e\n\n```\n- app\n  - config\n     database.yml\n     puma.rb\n  - docker\n    - app\n      Dockerfile\n    - web\n      Dockerfile\n      nginx.conf\n  docker-compose.yml\n  .dockerignore\n```\n\n- need to add `host: db` in `database.yml` in lieu of `host: localhost` when working with localhost \u0026 foreman\n\n- sequence `docker build .`, then `docker-compose up` one by one, `db`, then `sidekiq`, then `web`(otherwise you get an error due to `Bootsnap`).\n\n- the db is created, then `docker-compose exec web rails db:schema:load` and `db:seed`.\n\n- Note: need to set `POSTGRES_PASSWORD: xxx` in the service `web|environment`\n\n- get IPAdress with `docker inspect \u003ccontainerID\u003e | grep `IPAddress`(and the container Id is given in the list`docker ps -a`)\n\n```bash\nrm -rf tmp/*\ndocker rm $(docker ps -q -a) -f\ndocker rmi $(docker images -q) -f\ndocker build .\ndocker-compose up --build\ndocker-compose up -d web\ndocker-compose up -d sidekiq\ndocker-compose exec web rake db:create\ndocker-compose exec web rake db:schema:load\ndocker-compose exec web rake db:seeds\n```\n\nSet the key `host: db` in `database.yml` where `db` is the name of the Postgresql service in `docker-compose.yml`.\n\n\u003chttps://nickjanetakis.com/blog/dockerize-a-rails-5-postgres-redis-sidekiq-action-cable-app-with-docker-compose\u003e\n\nNeeds in `.env`:\n\n- Postgres:\n\n```\n# .env (Postgres Docker)\nPOSTGRES_USER=postgres\nPOTGRES_PASSWORD=postgres\n```\n\n- Redis:\n\nSetup with \u003chttps://manuelvanrijn.nl/sidekiq-heroku-redis-calc/\u003e\n\n```\n# .env\nREDIS_URL='redis://localhost:6379'\n```\n\nSet for Postgres:\n\n```\n# .env\n# Postgres Docker\nPOSTGRES_DB=godwd_development\nPOSTGRES_USER=postgres\nPOTGRES_PASSWORD=postgres\n```\n\n- create the database\n\n```bash\ndocker-compose exec app rails db:create\ndocker-compse exec app rails db:schema:load # instead of db:migrate\ndocker-compose exec app rails db:seed\n```\n\n- connect from local machine to a PSQL db in Docker:\n  \u003chttps://medium.com/better-programming/connect-from-local-machine-to-postgresql-docker-container-f785f00461a7\u003e\n\n## Docker commands\n\n\u003chttps://thoughtbot.com/upcase/videos/intro-to-docker \u003e\n\n- list all containers: `docker container ls -a`\n\n- list all containers's ids: `docker container ls -aq`\n\n- stop all containers by passing a list of ids: `docker container stop $(docker container ls -aq)`\n\n- remove all containers by passing a list of ids: `docker container rm $(docker container ls -aq)`\n\n- To wipe Docker clean and start from scratch, enter the command:\n  `docker container stop $(docker container ls –aq) \u0026\u0026 docker system prune –af ––volumes`\n\n`docker run --link db -it postgres:9.4 psql -h db -U postgres`\n\n# JWT, Knock\n\n\u003chttps://www.techandstartup.org/tutorials/rails-react-jwt\u003e\n\n\u003chttps://davidgay.org/programming/jwt-auth-rails-6-knock/\u003e\n\n`Knock` uses the gems `jwt` and we add the gem `bcrypt` for the `has_secure_password` attribute in the `User`model.\n\n- Install: `rails g\n\n```ruby\npayload = { id: 1, email: 'user@example.com' }\nsecret = Rails.application.credentials.secret_key_base\ntoken = JWT.encode(payload, secret, 'HS256')\n```\n\nbut we use the gem `Knock`\n\n# Heroku Nginx buildpack\n\n\u003chttps://elements.heroku.com/buildpacks/heroku/heroku-buildpack-nginx\u003e\n\nThe buildpack will not start NGINX until a file has been written to /tmp/app-initialized. Since NGINX binds to the dyno's $PORT and since the $PORT determines if the app can receive traffic, you can delay NGINX accepting traffic until your application is ready to handle it.\n\nFirst:\n\n- run `heroku buildpacks:add heroku-community/nginx`\n\n- copy the `nginx.config.erb` in the '/config' folder.\n\n- update the `puma.rb` code\n\n- Procfile: `bin/start-nginx bundle exec puma -C ./config/puma.rb`\n\n## Procfile\n\n```bash\nforeman start -f Procfile.dev\n```\n\n```\n#/Procfile (for Heroku)\nweb:  bin/start-nginx bundle exec puma -C ./config/puma.rb\nworker: bundle exec sidekiq -C ./config/sidekiq.yml\n\n```\n\n# Certbot - Nginx - Docker\n\n\u003chttps://medium.com/@pentacent/nginx-and-lets-encrypt-with-docker-in-less-than-5-minutes-b4b8a60d3a71\u003e\n\n# Old files\n\n```ruby\nclass RegisterJob \u003c ApplicationJob\n  queue_as :mailers\n  def perform(fb_user_email, fb_user_confirm_token)\n    UserMailer.register(fb_user_email, fb_user_confirm_token).deliver\n  end\nend\n```\n\n- version with ActiveJob : use \"RemoveDirectLink.perform_later\" in controller\n\n```ruby\nclass RemoveDirectLink \u003c ApplicationJob\n  queue_as :default\n  def perform(event_publicID)\n    auth = {\n      cloud_name: Rails.application.credentials.CL[:CLOUD_NAME],\n      api_key: Rails.application.credentials.CL[:API_KEY],\n      api_secret: Rails.application.credentials.CL[:API_SECRET]\n    }\n    return if !event_publicID\n    Cloudinary::Uploader.destroy(event_publicID, auth)\n  end\nend\n```\n\n# NGINX: reverse proxy\n\nThe main reason to set up Nginx as reverse proxy (client \u003e Nginx \u003e Puma/Rails) is to run your API server on a different network or IP then your front-end application is on. You can then secure this network and only allow traffic from the reverse proxy server.\n\n## localhost settings:\n\nThey are 2 ways to let Puma and Nginx communicate: with unix sockets and tcp/ip domain names.\n\n- unix socket:\n\n```\n#/config/puma.rb\n!! remove port 3001 (port where Rails listens to)\nbind \"unix:///Users/utilisateur/code/rails/godwd/tmp/sockets/nginx.socket\"\npreload_app!\nrackup      DefaultRackup\non_worker_boot { ActiveRecord::Base.establish_connection }\n\n\n#/usr/local/etc/nginx/nginx.conf\nhttp {\n  upstream app_server {\n    server unix:///Users/utilisateur/code/rails/godwd/tmp/sockets/nginx.socket fail_timeout=0;\n  }\n\n  server {\n    listen       8080;\n\n    location / {\n      proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;\n      proxy_set_header Host $http_host;\n      proxy_redirect off;\n      proxy_pass http://app_server;\n    }\n}\n```\n\n- tcp. (`127.0.0.1:3001` and not `0.0.0.:3001`):\n\n```\n#/app/config/puma.rb\nport 3001\n!!! remove bind \"unix://...\"\n\n#/usr/local/etc/nginx/nginx.conf\nhttp {\n  upstream app_server {\n    server 127.0.0.1:3001 fail_timeout=0;\n  }\n  server {\n    listen          8000;\n\n    location / {\n      proxy_set_header  X-Forwarded-For $proxy_add_x_forwarded_for;\n      proxy_set_header  Host $host;\n      proxy_redirect    off;\n      proxy_pass        http://app_server;\n    }\n  }\n}\n```\n\n- whitelisting `app_server` with: `Rails.application.config.host \u003c\u003c \"app_server\"` in '#/config.development.rb'.\n- Procfile `web: bundle exec puma -p 3001 -C config/puma.rb`\n\n## Heroku production.\n\nMy app is located at `godwd-api.herokuapp.com` and I name-spaced my endpoints with '/api/v1'.\n\n- buildpack : `$ heroku buildpacks:add heroku-community/nginx`,\n- add to `Procfile`: `web: bin/start-nginx bundle exec puma --config config/puma.rb`\n- add the file `/app/config/nginx.config.erb` using the biolerplate given by Heroku\n\n\u003e Mode tcp/ip\n\n```ruby\n# puma in single mode =\u003e set workers to 'O'\nworkers     ENV.fetch('WEB_CONCURRENCY') {2}\nthreads_count = Integer(ENV['RAILS_MAX_THREADS'] || 5)\nthreads threads_count, threads_count\n\nport        3001\npreload_app!\nrackup      DefaultRackup\n\n# Heroku buildpack needs this file to initialize\non_worker_fork { FileUtils.touch('/tmp/app-initialized') }\non_worker_boot { ActiveRecord::Base.establish_connection }\nplugin :tmp_restart\non_restart { Sidekiq.redis.shutdown(\u0026:close) }\n```\n\n```\n#/app/config/nginx.config.erb\ndaemon off;\n[...]\nhttp {\n  [...]\n  upstream app_server {\n    server 127.0.0.1:3001 fail_timeout=0;\n \t}\n\n  server {\n    listen \u003c%= ENV['PORT'] %\u003e;\n    [...]\n\n    location / {\n      try_files $uri @rails;\n    }\n\n    location / {\n      proxy_set_header  X-Real-IP  $remote_addr;\n      proxy_set_header  X-Forwarded-For $proxy_add_x_forwarded_for;\n      proxy_set_header Host $http_host;\n      proxy_redirect off;\n      proxy_pass http://app_server;\n    }\n  }\n}\n```\n\n## Nginx local mode\n\n```ruby\n#/config/puma.rb\n[...]\nport 3001  # mode tcp\nbind \"unix:///tmp/nginx.socket\" # mode unix socket\n[...]\n```\n\n```\n#/usr/local/etc/nginx/nginx.conf\n[...]\nhttp {\n    [...]\n    upstream app_server {\n      # mode tcp\n      server          localhost:3001 fail_timeout=0;\n      # mode unix\n      # server unix:///Users/utilisateur/code/rails/godwd/tmp/sockets/nginx.socket fail_timeout=0;\n    }\n    [...]\n    server {\n\n      listen 8080;\n      [...]\n      location / {\n        [...]\n        proxy_pass http://app_server; # same port as Puma\n      }\n}\n```\n\nTo use tcp connection between Nginx and Puma, use `foreman start -f Procfile_nginx_port`; it calls 'config.puma_port.rb'.\n\nFor unix socket connection, use `foreman start -f Procfile_nginx_socket` (calls 'config/puma_socket.rb')\n\nNavigate to http://localhost:8080/... and you should see 'server: nginx'\n\n\u003e check nginx with `ps aux | grep nginx``\n\nmauris_tovar mariana\n\n# Cloudfare / S3\n\n\u003chttps://www.blog.duomly.com/aws-course-lesson-1-how-to-host-website-on-s3-with-cloudflare/\u003e\n\n\u003chttps://support.cloudflare.com/hc/en-us/articles/360037983412-Configuring-an-Amazon-Web-Services-static-site-to-use-Cloudflare\u003e\n\n# Kill Rails\n\n'\u003cPID\u003e' = `lsof -i :5432` to see how is running at 5432, then `kill -9 \u003cPID\u003e`, or `kill -9 $(lsof -i :5432)`.\n\n- `which psql` and `pg_isready`\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fndrean%2Fgodwd","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fndrean%2Fgodwd","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fndrean%2Fgodwd/lists"}