diff --git a/Gemfile b/Gemfile index 0dcecef..33b625a 100644 --- a/Gemfile +++ b/Gemfile @@ -18,6 +18,9 @@ gem 'redis', '~> 4.0' # Use ActiveModel has_secure_password # gem 'bcrypt', '~> 3.1.7' +# Efficient ActionCable replacement to use web-sockets +gem 'anycable-rails', '~> 0.6', github: "anycable/anycable-rails", branch: "feature/rack-middleware" + # Use ActiveStorage variant # gem 'mini_magick', '~> 4.8' diff --git a/Gemfile.lock b/Gemfile.lock index 68490b6..984f58b 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,3 +1,12 @@ +GIT + remote: https://github.com/anycable/anycable-rails.git + revision: 59ebcbf9033725637b08dee5cbd5e26891dfc354 + branch: feature/rack-middleware + specs: + anycable-rails (0.6.4) + anycable (~> 0.6.0) + rails (>= 5) + GEM remote: https://rubygems.org/ specs: @@ -42,6 +51,10 @@ GEM i18n (>= 0.7, < 2) minitest (~> 5.1) tzinfo (~> 1.1) + anycable (0.6.3) + anyway_config (~> 1.4.2) + grpc (~> 1.17) + anyway_config (1.4.4) arel (9.0.0) bindex (0.8.1) bootsnap (1.4.4) @@ -55,6 +68,12 @@ GEM ffi (1.11.1) globalid (0.4.2) activesupport (>= 4.2.0) + google-protobuf (3.10.1) + googleapis-common-protos-types (1.0.4) + google-protobuf (~> 3.0) + grpc (1.25.0) + google-protobuf (~> 3.8) + googleapis-common-protos-types (~> 1.0) i18n (1.6.0) concurrent-ruby (~> 1.0) jbuilder (2.9.1) @@ -153,6 +172,7 @@ PLATFORMS ruby DEPENDENCIES + anycable-rails (~> 0.6)! bootsnap (>= 1.1.0) byebug jbuilder (~> 2.5) diff --git a/Procfile b/Procfile index 8f60e2a..4a649d8 100644 --- a/Procfile +++ b/Procfile @@ -1 +1,2 @@ -server: bin/rails server +web: bin/heroku-web +release: bundle exec rails db:migrate diff --git a/Procfile.dev b/Procfile.dev index ca3bc6b..432a2fd 100644 --- a/Procfile.dev +++ b/Procfile.dev @@ -1,2 +1,3 @@ server: bin/rails server assets: bin/webpack-dev-server +anycable: bundle exec anycable --server-command "anycable-go --port 3334" diff --git a/README.md b/README.md index cb2e17f..81629e3 100644 --- a/README.md +++ b/README.md @@ -16,16 +16,26 @@ If you are looking for code that reflects application at the end of any part, ta - [part-2 branch](https://github.com/demiazz/evil_chat/tree/part-2) for the Part 2; - [part-3 branch](https://github.com/demiazz/evil_chat/tree/part-3) for the Part 3. -# Installation +# Development installation +## When using localhost machine + +```sh +cp config/database.yml.example config/database.yml + +bundle install +bin/rails db:migrate +yarn install +brew install hivemind +brew install anycable-go +hivemind Procfile.dev ``` -$ cp config/database.yml.example config/database.yml -$ bin/rails credentials:edit +## When using Docker Compose -$ bundle install -$ bin/rails db:migrate -$ yarn install -$ brew install hivemind -$ hivemind Procfile.dev +```sh +brew install bibendi/dip/dip +eval "$(dip console)" +provision +rails s ``` diff --git a/app/channels/application_cable/connection.rb b/app/channels/application_cable/connection.rb index dde95a7..181bb61 100644 --- a/app/channels/application_cable/connection.rb +++ b/app/channels/application_cable/connection.rb @@ -3,7 +3,7 @@ class Connection < ActionCable::Connection::Base identified_by :current_user def connect - self.current_user = request.session.fetch("username", nil) + self.current_user = session.fetch("username", nil) reject_unauthorized_connection unless current_user end end diff --git a/app/views/layouts/application.html.erb b/app/views/layouts/application.html.erb index 839f820..2f9c6da 100644 --- a/app/views/layouts/application.html.erb +++ b/app/views/layouts/application.html.erb @@ -4,6 +4,7 @@ EvilChat <%= csrf_meta_tags %> <%= csp_meta_tag %> + <%= action_cable_meta_tag %> <%= stylesheet_pack_tag 'application', media: 'all' %> diff --git a/bin/heroku-web b/bin/heroku-web new file mode 100755 index 0000000..7d5b6a3 --- /dev/null +++ b/bin/heroku-web @@ -0,0 +1,7 @@ +#!/bin/bash + +if [ "$ANYCABLE_DEPLOYMENT" == "true" ]; then + bundle exec anycable --server-command="anycable-go" +else + bundle exec rails server -p $PORT -b 0.0.0.0 +fi diff --git a/config/anycable.yml b/config/anycable.yml new file mode 100644 index 0000000..f8d54a0 --- /dev/null +++ b/config/anycable.yml @@ -0,0 +1,30 @@ +# This file contains per-environment settings for AnyCable. +# +# Since AnyCable config is based on anyway_config (https://github.com/palkan/anyway_config), all AnyCable settings +# can be set or overridden through the corresponding environment variables. +# E.g., `rpc_host` is overridden by ANYCABLE_RPC_HOST, `debug` by ANYCABLE_DEBUG etc. +# +# Note that AnyCable recognizes REDIS_URL env variable for Redis pub/sub adapter. If you want to +# use another Redis instance for AnyCable, provide ANYCABLE_REDIS_URL variable. +# +# Read more about AnyCable configuration here: https://docs.anycable.io/#/ruby/configuration +# +default: &default + # Turn on/off access logs ("Started..." and "Finished...") + access_logs_disabled: false + # This is the host and the port to run AnyCable RPC server on. + # You must configure your WebSocket server to connect to it, e.g.: + # $ anycable-go --rpc-host=":50051" + rpc_host: "127.0.0.1:50051" + # Whether to enable gRPC level logging or not + log_grpc: false + # Use the same channel name for WebSocket server, e.g.: + # $ anycable-go --redis-channel="__anycable__" + redis_channel: "__anycable__" + +development: + <<: *default + redis_url: "redis://localhost:6379/1" + +production: + <<: *default diff --git a/config/cable.yml b/config/cable.yml index 1876ed7..8a279ad 100644 --- a/config/cable.yml +++ b/config/cable.yml @@ -1,10 +1,11 @@ +# Make it possible to switch adapters by passing the CABLE_ADAPTER env variable. +# For example, you can use it fallback to the standard Action Cable in staging/review +# environments (by setting `CABLE_ADAPTER=redis`). development: - adapter: async + adapter: <%= ENV.fetch("CABLE_ADAPTER", "any_cable") %> test: - adapter: async + adapter: test production: - adapter: redis - url: <%= ENV.fetch("REDIS_URL") { "redis://localhost:6379/1" } %> - channel_prefix: evil_chat_production + adapter: <%= ENV.fetch("CABLE_ADAPTER", "any_cable") %> diff --git a/config/environments/development.rb b/config/environments/development.rb index d52ec9e..9407425 100644 --- a/config/environments/development.rb +++ b/config/environments/development.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + Rails.application.configure do # Settings specified here will take precedence over those in config/application.rb. @@ -51,4 +53,7 @@ # Use an evented file watcher to asynchronously detect changes in source code, # routes, locales, etc. This feature depends on the listen gem. config.file_watcher = ActiveSupport::EventedFileUpdateChecker + + # Specify AnyCable WebSocket server URL to use by JS client + config.action_cable.url = ENV.fetch("CABLE_URL", "ws://localhost:3334/cable").presence end diff --git a/config/environments/production.rb b/config/environments/production.rb index 6026643..d0bdb2d 100644 --- a/config/environments/production.rb +++ b/config/environments/production.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + Rails.application.configure do # Settings specified here will take precedence over those in config/application.rb. @@ -16,7 +18,7 @@ # Ensures that a master key has been made available in either ENV["RAILS_MASTER_KEY"] # or in config/master.key. This key is used to decrypt credentials (and other encrypted files). - config.require_master_key = true + # config.require_master_key = true # Disable serving static files from the `/public` folder by default since # Apache or NGINX already handles this. @@ -35,6 +37,8 @@ # Mount Action Cable outside main process or domain # config.action_cable.mount_path = nil # config.action_cable.url = 'wss://example.com/cable' + # Specify AnyCable WebSocket server URL to use by JS client + config.action_cable.url = ENV["CABLE_URL"].presence # config.action_cable.allowed_request_origins = [ 'http://example.com', /http:\/\/example.*/ ] # Force all access to the app over SSL, use Strict-Transport-Security, and use secure cookies. diff --git a/config/master.key.back b/config/master.key.back deleted file mode 100644 index 8b63c1e..0000000 --- a/config/master.key.back +++ /dev/null @@ -1 +0,0 @@ -533269e346eb9fd9dc4adc9807749c03 \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml index 09fde98..ee98a08 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -34,7 +34,9 @@ services: EDITOR: vim RAILS_ENV: ${RAILS_ENV:-development} DATABASE_URL: postgres://postgres:postgres@postgres:5432 - REDIS_URL: redis://redis:6379/ + REDIS_URL: redis://redis:6379/0 + ANYCABLE_REDIS_URL: redis://redis:6379/0 + ANYCABLE_RPC_HOST: 0.0.0.0:50051 WEBPACKER_DEV_SERVER_HOST: webpacker DISABLE_SPRING: 1 depends_on: @@ -55,6 +57,10 @@ services: condition: service_healthy webpacker: condition: service_started + anycable-ws: + condition: service_started + anycable-rpc: + condition: service_started webpacker: <<: *app @@ -69,6 +75,24 @@ services: RAILS_ENV: ${RAILS_ENV:-development} WEBPACKER_DEV_SERVER_HOST: 0.0.0.0 + anycable-ws: + image: anycable/anycable-go:v0.6.4 + ports: + - '3334:3334' + environment: + PORT: 3334 + ANYCABLE_REDIS_URL: redis://redis:6379/0 + ANYCABLE_RPC_HOST: anycable-rpc:50051 + depends_on: + - anycable-rpc + - redis + + anycable-rpc: + <<: *backend + command: bundle exec anycable + ports: + - '50051' + postgres: image: postgres:12 volumes: