0
0

More than 3 years have passed since last update.

Railsアプリに隠しコマンド・隠しページを実装してみる

Last updated at Posted at 2021-06-23

概要

Railsアプリを作っている際、特定のコマンドを知っている人だけが辿り着けるページみたいなものがあれば面白いなと思い調べてみたところ、JavaScriptを上手く使えば実装できる事がわかったので試してみました。

実運用できるレベルかどうかは別として完全に遊び心みたいな感じです。(URLを直打ちすれば普通に飛べちゃうみたいな野暮なツッコミは無しでお願いします(笑))

完成イメージ

マイ-ムービー(7).gif

今回はかの有名な コナミコマンド (上上下下右左右左BA)を入力した場合、隠しページにつながる導線をアラートメッセージで表示するような仕組みにしてみました。

仕様

  • Ruby3
  • Rails6
  • MySQL8
  • Docker

再現性を考慮しDockerで環境構築を行っていきます。

実装

前置きはそこそこに実装に入ります。

ディレクトリ&各種ファイルを作成

$ mkdir rails-secret-command && cd rails-secret-command
$ touch Dockerfile
$ touch docker-compose.yml
$ touch entrypoint.sh
$ touch Gemfile
$ touch Gemfile.lock
./Dockerfile
FROM ruby:3.0

RUN curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | apt-key add - \
    && echo "deb https://dl.yarnpkg.com/debian/ stable main" | tee /etc/apt/sources.list.d/yarn.list

RUN apt-get update -qq && apt-get install -y build-essential libpq-dev nodejs yarn

ENV APP_PATH /myapp

RUN mkdir $APP_PATH
WORKDIR $APP_PATH

COPY Gemfile $APP_PATH/Gemfile
COPY Gemfile.lock $APP_PATH/Gemfile.lock
RUN bundle install

COPY . $APP_PATH

COPY entrypoint.sh /usr/bin/
RUN chmod +x /usr/bin/entrypoint.sh
ENTRYPOINT ["entrypoint.sh"]
EXPOSE 3000

CMD ["rails", "server", "-b", "0.0.0.0"]
./docker-compose.yml
version: "3"
services:
  db:
    image: mysql:8.0
    environment:
      MYSQL_ROOT_PASSWORD: password
    command: --default-authentication-plugin=mysql_native_password
    volumes:
      - mysql-data:/var/lib/mysql
      - /tmp/dockerdir:/etc/mysql/conf.d/
    ports:
      - 3306:3306
  web:
    build:
      context: .
      dockerfile: Dockerfile
    command: bash -c "rm -f tmp/pids/server.pid && bundle exec rails s -p 3000 -b '0.0.0.0'"
    volumes:
      - .:/myapp
      - ./vendor/bundle:/myapp/vendor/bundle
    environment:
      TZ: Asia/Tokyo
      RAILS_ENV: development
    ports:
      - 3000:3000
    depends_on:
      - db
volumes:
  mysql-data:
./entrypoint.sh
#!/bin/bash
set -e

# Remove a potentially pre-existing server.pid for Rails.
rm -f /myapp/tmp/pids/server.pid

# Then exec the container's main process (what's set as CMD in the Dockerfile).
exec "$@"
./Gemfile
# frozen_string_literal: true

source "https://rubygems.org"

git_source(:github) {|repo_name| "https://github.com/#{repo_name}" }

gem "rails", "~> 6"
/Gemfile.lock
# 空欄でOK

rails new

おなじみのコマンドでプロジェクトを作成します。

$ docker-compose run web rails new . --force --no-deps -d mysql

Gemfileが更新されたので再ビルド。

$ docker-compose build

「.config/database.yml」を編集

./config/database.yml
default: &default
  adapter: mysql2
  encoding: utf8mb4
  pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
  username: root
  password: password # デフォルトだと空欄になっているはずなので変更
  host: db # デフォルトだとlocalhostになっているはずなので変更

development:
  <<: *default
  database: myapp_development

test:
  <<: *default
  database: myapp_test

production:
  <<: *default
  database: <%= ENV["DATABASE_NAME"] %>
  username: <%= ENV["DATABASE_USERNAME"] %>
  password: <%= ENV["DATABASE_PASSWORD"] %>
  host: <%= ENV["DATABASE_HOST"] %>

データベースを作成

$ docker-compose run web rails db:create

動作確認

$ docker-compose up -d

スクリーンショット 2021-05-04 4.44.25.png

localhost:3000 にアクセスしていつもの画面が表示されればOK。

トップページを作成

$ docker-compose run web rails g controller home index

コントローラー

./app/controllers/home_controller.rb
class HomeController < ApplicationController
  def index
  end
end

ビュー

./app/views/home/index.html.erb
<%= javascript_pack_tag "home/index" %>

<h1>次のコマンドを入力してください</h1>
<p>上上下下右左右左BA</p>

ルーティング

./config/routes.rb
Rails.application.routes.draw do
  root "home#index" # 追記
end

隠しページを作成

$ docker-compose run web rails g controller secret_pages index

コントローラー

./app/controllers/secret_pages_controller.rb
class SecretPagesController < ApplicationController
  def index
  end
end

ビュー

./app/views/secret_pages/index.html.erb
<h1>ここは隠しページです</h1>

ルーティング

./config/routes.rb
Rails.application.routes.draw do
  root "home#index"
  resources :secret_pages, only: %i[index] # 追記
end

隠しコマンドを実装

Rails6でJavaScriptを使用する場合、「app/javascript/packs/」以下にjsファイルを配置する必要があるのでちゃちゃっと作成します。

$ mkdir app/javascript/packs/home
$ touch app/javascript/packs/home/index.js
./app/javascript/packs/home/index.js
var secretCommands = [38, 38, 40, 40, 37, 39, 37, 39, 66, 65] // 隠しコマンド
var inputtedCommands = [] // 入力されたコマンド

document.onkeydown = function(e) {
  inputtedCommands.push(e.keyCode)

  // 入力されたコマンドの順番と値が隠しコマンドと全一致した場合はアラートメッセージを表示
  if (inputtedCommands[inputtedCommands.length - 1] === secretCommands[inputtedCommands.length - 1]) {
    if (inputtedCommands.length === secretCommands.length) {
      if (window.confirm("隠しページへ飛びますか?")) {
        location.href = "http://localhost:3000/secret_pages" // 隠しページへ飛ぶ
      } else {
        window.alert("キャンセルしました") // キャンセル時の処理
      }
      inputtedCommands.length = 0
    }
  } else {
    inputtedCommands.length = 0 // 少しでも違った場合はリセット
  }

  console.log(inputtedCommands)
}

JavaScriptには

  • keydown(キーが押された瞬間)
  • keypress(キーが押されて文字が入力された瞬間)
  • keyup(キーを放した瞬間)

といったキーボードの操作を検知して発火するイベントがありますが、今回はキーが押された瞬間を検知したいので「keydown」を採用しました。

また、キーボードにはそれぞれのキーに「キーコード」という数字が割り当てられています。

参照: キーコード一覧

そのルールに従うと「上上下下右左右左BA」は「38, 38, 40, 40, 37, 39, 37, 39, 66, 65」に変換できるため、コンピューターに対してはコイツが隠しコマンドであると認識させます。

動作確認

マイ-ムービー(7).gif

あとがき

以上、Railsアプリに隠しコマンド・隠しページを実装してみました。

実用性という点ではちょっと不明ですが、こういう面白い仕掛けを組み込む事もできるという内容です。

今回作成したコードの完成品を記載しておくので、もし記事通りに進めて動かない部分などはそちらと照らし合わせてみてください。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0