text:
# frozen_string_literal: true

class AddIsIgnoredToIgnoredUsers < ActiveRecord::Migration[5.0]
  def change
    add_column :ignored_users, :is_ignored, :boolean
  end
end


text:
# frozen_string_literal: true

class AddIsCompletedToFlags < ActiveRecord::Migration[5.0]
  def change
    add_column :flags, :is_completed, :boolean, default: false
  end
end

repo_name: Charcoal-SE/metasmoke

repo_language: Ruby

mime_type: text/plain


text:
# frozen_string_literal: true

class RemoveRedundantSiteSettings < ActiveRecord::Migration[5.2]
  def change
    SiteSetting.find_by(name: 'core_threshold').destroy
    SiteSetting.find_by(name: 'core_time_period').destroy
  end
end


text:
# frozen_string_literal: true

class FixInvalidFeedbacksV2 < ActiveRecord::Migration[5.2]
  def up
    fixed_feedbacks = 0
    Feedback.where.not(legacy_feedback_type: nil).each do |feedback|
      normalized_feedback = case feedback.legacy_feedback_type.downcase
                            when /^true[a-z][-=].*/ then 'tpu-'
                            when /^true[a-z].*/ then 'tpu'
                            when /^true[-=].*/ then 'tp-'
                            when /^true.*/ then 'tp'
                            when /^false[a-z][-=].*/ then 'fpu-'
                            when /^false[a-z].*/ then 'fpu'
                            when /^false[-=].*/ then 'fp-'
                            when /^false.*/ then 'fp'
                            when /^tp[a-z][-=].*/ then 'tpu-'
                            when /^tp[a-z].*/ then 'tpu'
                            when /^fp[a-z][-=].*/ then 'tpu-'
                            when /^fp[a-z].*/ then 'tpu'
                            when /^naa[-=].*/ then 'naa-'
                            when /^naa.*/ then 'naa'
                            when /^n/ then 'naa-'
                            when /^f/ then 'fp-'
                            when /^k/ then 'tp-'
                            when /^ignore[-=]/ then 'ignore-'
                            when /^ignore/ then 'ignore'
                            else; 'invalid'
                            end
      puts "#{feedback.legacy_feedback_type}\t#{normalized_feedback}"
      next unless feedback.feedback_type != normalized_feedback
      # Skip validation
      feedback.update_columns(feedback_type: normalized_feedback)
      fixed_feedbacks += 1
    end
    puts "Fixed #{fixed_feedbacks} feedbacks"
  end

  def down
    raise ActiveRecord::IrreversibleMigration
  end
end


text:
class CreateNewViews < ActiveRecord::Migration[5.2]
  def change
    success = system 'rails runner db/scripts/create_views.rb'
    raise StandardError, 'View script had non-zero exit code' unless success
  end
end


text:
# frozen_string_literal: true

class AddDescriptionToPostsReviewQueue < ActiveRecord::Migration[5.2]
  def change
    ReviewQueue['posts'].update(description: 'Review new reports as they come in and add feedback to them.')
  end
end


text:
# frozen_string_literal: true

class CreateAnnouncements < ActiveRecord::Migration[5.1]
  def change
    create_table :announcements do |t|
      t.string :text
      t.datetime :expiry

      t.timestamps
    end
  end
end


text:
class CreateDomainGroups < ActiveRecord::Migration[5.2]
  def change
    create_table :domain_groups do |t|
      t.string :name
      t.string :regex

      t.timestamps
    end
  end
end


text:
# frozen_string_literal: true

class CreateAbuseComments < ActiveRecord::Migration[5.2]
  def change
    create_table :abuse_comments do |t|
      t.references :user, foreign_key: true
      t.references :abuse_report, foreign_key: true
      t.text :text

      t.timestamps
    end
  end
end


text:
# frozen_string_literal: true

class RemoveOrderingFromAbuseReportStatuses < ActiveRecord::Migration[5.2]
  def change
    remove_column :abuse_report_statuses, :ordering, :integer
  end
end


text:
# frozen_string_literal: true

class AddEnabled2faToUsers < ActiveRecord::Migration[5.1]
  def change
    add_column :users, :enabled_2fa, :boolean
  end
end


text:
# frozen_string_literal: true

class AddDescriptionToReviewQueues < ActiveRecord::Migration[5.2]
  def change
    add_column :review_queues, :description, :text
  end
end


text:
# frozen_string_literal: true

class AddLastUsersUpdateToSites < ActiveRecord::Migration[5.1]
  def change
    add_column :sites, :last_users_update, :datetime
  end
end


text:
# frozen_string_literal: true

class CreateReviewItems < ActiveRecord::Migration[5.2]
  def change
    create_table :review_items do |t|
      t.references :review_queue, foreign_key: true
      t.references :reviewable, polymorphic: true

      t.timestamps
    end
  end
end


text:
# frozen_string_literal: true

class AddTagsToPosts < ActiveRecord::Migration[5.1]
  def change
    add_column :posts, :tags, :string
  end
end


text:
# frozen_string_literal: true

class AddPinnedToUsersRoles < ActiveRecord::Migration[5.2]
  def change
    add_column :users_roles, :pinned, :boolean, default: false
  end
end


text:
# frozen_string_literal: true

class AddLastPostTitleToReason < ActiveRecord::Migration[4.2]
  def change
    add_column :reasons, :last_post_title, :string
  end
end


text:
# frozen_string_literal: true

class CreateFlagConditionsSitesJoinTable < ActiveRecord::Migration[5.0]
  def change
    create_table 'flag_conditions_sites' do |t|
      t.integer :flag_condition_id
      t.integer :site_id
    end
  end
end


text:
# frozen_string_literal: true

class RemoveIpColumnsFromUser < ActiveRecord::Migration[5.0]
  def change
    remove_column :users, :last_sign_in_ip
    remove_column :users, :current_sign_in_ip
  end
end


text:
# frozen_string_literal: true

class CreateSiteSettings < ActiveRecord::Migration[5.2]
  def change
    create_table :site_settings do |t|
      t.string :name
      t.string :value
      t.string :value_type

      t.timestamps
    end
  end
end


text:
# frozen_string_literal: true

class CreateBlacklistedWebsites < ActiveRecord::Migration[5.0]
  def change
    create_table :blacklisted_websites do |t|
      t.string :host
      t.boolean :is_active, default: true

      t.timestamps
    end
  end
end


text:
class DropTokenMigratedFromUsers < ActiveRecord::Migration[5.2]
  def change
    rename_column :users, :token_migrated, :token_migrated_legacy
  end
end


text:
# frozen_string_literal: true

class AddEncryptedAPITokenToUsers < ActiveRecord::Migration[5.1]
  def change
    add_column :users, :encrypted_api_token, :string
  end
end


text:
# frozen_string_literal: true

class AddPostLinkToFeedback < ActiveRecord::Migration[4.2]
  def change
    add_column :feedbacks, :post_link, :string
  end
end


text:
# frozen_string_literal: true

class RemoveNeedsAdminAndReasonFromPosts < ActiveRecord::Migration[5.0]
  def change
    remove_column :posts, :needs_admin, :string
    remove_column :posts, :admin_reason, :string
  end
end


text:
# frozen_string_literal: true

class AddIsApprovedToUser < ActiveRecord::Migration[4.2]
  def change
    add_column :users, :is_approved, :bool
  end
end


text:
# frozen_string_literal: true

class AddIsDryRunToFlagLogs < ActiveRecord::Migration[5.0]
  def change
    add_column :flag_logs, :is_dry_run, :boolean
  end
end


text:
# frozen_string_literal: true

class MoveMissedUsersToStackExchangeUsers < ActiveRecord::Migration[4.2]
  def change
    Post.where('stack_exchange_user_id IS NULL').each do |post|
      next if post.user_link.nil?

      user_id = nil

      begin
        user_id = post.user_link.scan(%r{/u/(\d*)}).first.first
      rescue
        next
      end

      hash = { site_id: post.site_id, user_id: user_id }
      se_user = StackExchangeUser.find_or_create_by(hash)
      se_user.reputation = post.user_reputation
      se_user.username = post.username

      post.stack_exchange_user = se_user

      se_user.save!
      post.save!
    end
  end
end


text:
# frozen_string_literal: true

class CreateStackExchangeUsers < ActiveRecord::Migration[4.2]
  def change
    create_table :stack_exchange_users do |t|
      t.integer :user_id
      t.string :username
      t.datetime :last_api_update
      t.boolean :still_alive, default: true
      t.integer :answer_count
      t.integer :question_count
      t.integer :reputation

      t.timestamps null: false
    end
  end
end


text:
# frozen_string_literal: true

class AddKeyToAPIKeys < ActiveRecord::Migration[5.0]
  def change
    add_column :api_keys, :key, :string
  end
end


text:
# frozen_string_literal: true

class UpdateTablesToUtf8Mb4 < ActiveRecord::Migration[5.0]
  def change
    [Post, StackExchangeUser].each do |m|
      execute "ALTER TABLE #{m.table_name} CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci"
    end

    posts_column_types = Post.columns_hash

    # Post table columns
    execute "ALTER TABLE `posts` MODIFY `title` #{posts_column_types['title'].sql_type} CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci"
    execute "ALTER TABLE `posts` MODIFY `body` #{posts_column_types['body'].sql_type} CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci"
    execute "ALTER TABLE `posts` MODIFY `username` #{posts_column_types['username'].sql_type} CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci"

    # StackExchangeUser table columns
    # rubocop:disable Metrics/LineLength
    execute "ALTER TABLE `stack_exchange_users` MODIFY `username` #{posts_column_types['username'].sql_type} CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci"
    # rubocop:enable Metrics/LineLength
  end
end


text:
class AddMailerToEmailsTypes < ActiveRecord::Migration[5.2]
  def change
    add_column :emails_types, :mailer, :string
  end
end


text:
# frozen_string_literal: true

class FixedMixedContentErrors < ActiveRecord::Migration[4.2]
  def change
    Site.all.each do |site|
      site.site_logo.gsub!(/http:/, '')
      site.save!
    end
  end
end


text:
class AddTypeToAPIKeys < ActiveRecord::Migration[5.2]
  def change
    add_column :api_keys, :key_type, :string, null: false, default: 'standard'
  end
end


text:
# frozen_string_literal: true

class CreateAbuseContacts < ActiveRecord::Migration[5.2]
  def change
    create_table :abuse_contacts do |t|
      t.string :name
      t.string :email
      t.string :link
      t.text :details

      t.timestamps
    end
  end
end


text:
# frozen_string_literal: true

class AddDefaultToPreferencesFlagsUsed < ActiveRecord::Migration[5.0]
  def change
    change_column :user_site_settings, :flags_used, :integer, default: 0
  end
end


text:
# frozen_string_literal: true

class DuplicateFeedbackOnOldPosts < ActiveRecord::Migration[5.2]
  def change
    ActiveRecord::Base.connection.execute 'INSERT INTO feedbacks (user_name, feedback_type, post_id, user_id) '\
                                          "SELECT 'System' AS 'user_name', feedbacks.feedback_type, feedbacks.post_id, -1 AS 'user_id' "\
                                          'FROM feedbacks INNER JOIN posts ON feedbacks.post_id = posts.id WHERE posts.feedbacks_count = 1;'
  end
end


text:
# frozen_string_literal: true

class AddIndexOnDeletionLogs < ActiveRecord::Migration[5.0]
  def change
    add_index :deletion_logs, :post_id, name: 'post_id_ix'
  end
end


text:
# frozen_string_literal: true

class AddStackExchangeAccountIdToUser < ActiveRecord::Migration[5.0]
  def change
    add_column :users, :stack_exchange_account_id, :integer
  end
end


text:
# frozen_string_literal: true

class InstallBlazer < ActiveRecord::Migration[5.2]
  def change
    create_table :blazer_queries do |t|
      t.references :creator
      t.string :name
      t.text :description
      t.text :statement
      t.string :data_source
      t.timestamps null: false
    end

    create_table :blazer_audits do |t|
      t.references :user
      t.references :query
      t.text :statement
      t.string :data_source
      t.timestamp :created_at
    end

    create_table :blazer_dashboards do |t|
      t.references :creator
      t.text :name
      t.timestamps null: false
    end

    create_table :blazer_dashboard_queries do |t|
      t.references :dashboard
      t.references :query
      t.integer :position
      t.timestamps null: false
    end

    create_table :blazer_checks do |t|
      t.references :creator
      t.references :query
      t.string :state
      t.string :schedule
      t.text :emails
      t.string :check_type
      t.text :message
      t.timestamp :last_run_at
      t.timestamps null: false
    end
  end
end


text:
version: 2
jobs:
  js:
    working_directory: ~/Charcoal-SE/metasmoke
    parallelism: 1
    shell: /bin/bash --login

    docker:
    - image: cimg/ruby:2.7.4-node

    steps:
    - checkout
    - run: if [ -z "${NODE_ENV:-}" ]; then export NODE_ENV=test; fi
    - run: export PATH="~/Charcoal-SE/metasmoke/node_modules/.bin:$PATH"
    - restore_cache:
        keys:
        - v1-dep-node-{{ .Branch }}-
        - v1-dep-node-master-
        - v1-dep-node-
    - run: yarn install
    - save_cache:
        key: v1-dep-node-{{ .Branch }}-{{ epoch }}
        paths:
        - ./node_modules
    - run: echo -e "export RAILS_ENV=test\nexport RACK_ENV=test" >> $BASH_ENV
    - run: npm test

  rubocop:
    working_directory: ~/Charcoal-SE/metasmoke
    parallelism: 1
    shell: /bin/bash --login

    environment:
      CIRCLE_ARTIFACTS: /tmp/circleci-artifacts
      CIRCLE_TEST_REPORTS: /tmp/circleci-test-results

    docker:
    - image: cimg/ruby:2.7.4-node
    - image: cimg/mysql:5.7.36
      environment:
        MYSQL_ROOT_HOST: '%'
        MYSQL_USER: 'ubuntu'
        MYSQL_PASSWORD: 'ubuntu'
        MYSQL_DATABASE: 'metasmoke_test'

    steps:
    - run: sudo apt-get update
    - run: sudo apt-get install apt-utils redis-server libsqlite3-dev
    - run: sudo service redis-server start
    - checkout
    - run: mkdir -p $CIRCLE_ARTIFACTS $CIRCLE_TEST_REPORTS
    - run: cp config/config.sample.yml config/config.yml
    - restore_cache:
        keys:
        - v1-dep-bundle-{{ .Branch }}-
        - v1-dep-bundle-master-
        - v1-dep-bundle-
    - run: gem install bundler:1.17.3
    - run: 'bundle check --path=vendor/bundle || bundle install --path=vendor/bundle
        --jobs=4 --retry=3 '
    - save_cache:
        key: v1-dep-bundle-{{ .Branch }}-{{ epoch }}
        paths:
        - vendor/bundle
        - ~/.bundle
    - run: bundle exec rubocop -D

  rails-tests:
    working_directory: ~/Charcoal-SE/metasmoke
    parallelism: 1
    shell: /bin/bash --login

    environment:
      CIRCLE_ARTIFACTS: /tmp/circleci-artifacts
      CIRCLE_TEST_REPORTS: /tmp/circleci-test-results

    docker:
    - image: cimg/ruby:2.7.4-node
    - image: cimg/mysql:5.7.36
      environment:
        MYSQL_ROOT_HOST: '%'
        MYSQL_USER: 'ubuntu'
        MYSQL_PASSWORD: 'ubuntu'
        MYSQL_DATABASE: 'metasmoke_test'

    steps:
    - run: sudo apt-get update
    - run: sudo apt-get install apt-utils redis-server libsqlite3-dev
    - run: sudo service redis-server start
    - checkout
    - run: mkdir -p $CIRCLE_ARTIFACTS $CIRCLE_TEST_REPORTS
    - run: cp config/config.sample.yml config/config.yml
    - run: echo -e "export RAILS_ENV=test\nexport RACK_ENV=test" >> $BASH_ENV
    - run: |-
        mkdir -p config && echo 'test:
          adapter: mysql2
          database: metasmoke_test
          username: ubuntu
          password: ubuntu
          host: 127.0.0.1
        ' > config/database.yml
    - run: |-
        mkdir -p config && echo 'test:
          adapter: redis
          timeout: 120
          url: redis://localhost:6379
        ' > config/cable.yml

    # Bundler
    - restore_cache:
        keys:
        - v1-dep-bundle-{{ .Branch }}-
        - v1-dep-bundle-master
        - v1-dep-bundle
    - run: gem install bundler:1.17.3
    - run: 'bundle check --path=vendor/bundle || bundle install --path=vendor/bundle
        --jobs=4 --retry=3 '
    - save_cache:
        key: v1-dep-bundle-{{ .Branch }}-{{ epoch }}
        paths:
        - vendor/bundle
        - ~/.bundle

    # Node deps
    - restore_cache:
        keys:
        - v1-dep-node-{{ .Branch }}-
        - v1-dep-node-master
        - v1-dep-node-
    - run: yarn install
    - save_cache:
        key: v1-dep-node-{{ .Branch }}-{{ epoch }}
        paths:
        - ./node_modules

    # Run tests
    - run:
        command: bundle exec rake db:create db:schema:load db:test:prepare db:migrate --trace
        environment:
          RAILS_ENV: test
          RACK_ENV: test
    - run:
        command: bundle exec rails test --verbose
        environment:
          RAILS_ENV: test
          RACK_ENV: test
          DISABLE_QUERY_LOGGER: true

    - store_artifacts:
        path: log
    - store_test_results:
        path: /tmp/circleci-test-results
    - store_artifacts:
        path: /tmp/circleci-artifacts
    - store_artifacts:
        path: coverage
    - store_artifacts:
        path: /tmp/circleci-test-results

workflows:
  version: 2
  build_and_test:
    jobs:
      - js
      - rails-tests
      - rubocop


text:
# frozen_string_literal: true

every 1.day, at: '1:00 am' do
  runner 'ReasonsHelper.check_for_inactive_reasons'
  runner 'ReasonsHelper.calculate_weights_for_flagging'
end

every 1.day, at: '6:00 pm' do
  runner 'SitesHelper.update_sites'
end

# Run moderator check about an hour before the spam waves.
# We don't want to run it *during* the spam wave,
# as that could cause us to get unrespected API backoffs.

every 1.day, at: '2:00 am' do
  runner 'User.where.not(stack_exchange_account_id: nil).each { |u| u.update_moderator_sites; sleep(5) }'
  runner 'FlagCondition.revalidate_all'
  runner 'AbuseReport.update_stale_reports'
  runner 'ConflictingFeedbackJob.perform_later'
end

# every 1.day, at: '3:10am' do
#   runner 'ApplicationRecord.full_dump'
# end

every 1.day, at: '3:00am' do
  runner 'ScheduledMailJob.perform_later'
end


text:
# frozen_string_literal: true

# Puma can serve each request in a thread from an internal thread pool.
# The `threads` method setting takes two numbers: a minimum and maximum.
# Any libraries that use thread pools should be configured to match
# the maximum value specified for Puma. Default is set to 5 threads for minimum
# and maximum; this matches the default thread size of Active Record.
#
threads_count = ENV.fetch('RAILS_MAX_THREADS') { 5 }
threads threads_count, threads_count

# Specifies the `port` that Puma will listen on to receive requests; default is 3000.
#
port        ENV.fetch('PORT') { 3000 }

# Specifies the `environment` that Puma will run in.
#
environment ENV.fetch('RAILS_ENV') { 'development' }

# Specifies the number of `workers` to boot in clustered mode.
# Workers are forked webserver processes. If using threads and workers together
# the concurrency of the application would be max `threads` * `workers`.
# Workers do not work on JRuby or Windows (both of which do not support
# processes).
#
# workers ENV.fetch("WEB_CONCURRENCY") { 2 }

# Use the `preload_app!` method when specifying a `workers` number.
# This directive tells Puma to first boot the application and load code
# before forking the application. This takes advantage of Copy On Write
# process behavior so workers use less memory.
#
# preload_app!

# Allow puma to be restarted by `rails restart` command.
plugin :tmp_restart


text:
production:
  adapter: redis
  url: redis://3.81.154.25:6379

local: &local
  adapter: redis
  timeout: 120
  url: redis://localhost:6378

test:
  adapter: redis
  timeout: 200
  url: redis://localhost:6379

development: *local


text:
# see https://github.com/ankane/blazer for more info

data_sources:
  main:
    url: mysql2://metasmoke_blazer:zFpc8tw7CdAuXizX@localhost:3306/metasmoke_dev
    timeout: 10
    cache:
      mode: slow
      expires_in: 60
      slow_threshold: 5

    smart_variables:
      reason_id: "SELECT id, reason_name FROM p_reasons ORDER BY id ASC"
      user_id: "SELECT id, username FROM p_users ORDER BY id ASC"
      api_key_id: "SELECT id, app_name FROM p_api_keys ORDER BY id ASC"
      site_id: "SELECT id, site_name FROM p_sites ORDER BY id ASC"
      domain_tag_id: "SELECT id, name FROM p_domain_tags ORDER BY id ASC"

    linked_columns:
      # user_id: "/admin/users/{value}"

    smart_columns:
      reason_id: "SELECT id, reason_name FROM p_reasons WHERE id IN {value}"
      user_id: "SELECT id, username FROM p_users WHERE id IN {value}"
      api_key_id: "SELECT id, app_name FROM p_api_keys WHERE id IN {value}"
      site_id: "SELECT id, site_name FROM p_sites WHERE id IN {value}"
      domain_tag_id: "SELECT id, name FROM p_domain_tags WHERE id IN {value}"

audit: true
user_class: User
user_method: current_user
user_name: username


text:
common: &common
  redis_logging:
    adapter: redis
    url: redis://localhost:6378
  token_store:
    host: https://the.best.token.store
    key: atokenforthetokenstore
  smoke_detector:
    key: 'smokey_key'
  ses_smtp_credentials: # email
    username: 'xxx'
    password: 'yyy'
  aws:
    access_token: ''
    secret_token: ''
  github:
    secret_token: 'xxyyzz' # the webhook token
    access_token: 'deadbeef' # or username & password, if you prefer
  stack_exchange:
    client_secret: 'sekrit'
    client_id: '1234'
    key: 'key'
    redirect_uri: 'https://domain.com/authentication/redirect_target'
    login_redirect_uri: 'https://domain.com/authentication/login_redirect_target'
    token_aes_key: 'random_bytes_here'
  travis_ci:
    token: 'gibberish'
  api_field_mappings:
    - announcements.id
    - announcements.text
    - announcements.expiry
    - announcements.created_at
    - announcements.updated_at
    - api_keys.id
    - api_keys.created_at
    - api_keys.updated_at
    - api_keys.app_name
    - api_keys.user_id
    - api_keys.github_link
    - api_keys.is_trusted
    - audits.id
    - audits.auditable_id
    - audits.auditable_type
    - audits.associated_id
    - audits.associated_type
    - audits.user_id
    - audits.user_type
    - audits.username
    - audits.action
    - audits.audited_changes
    - audits.version
    - audits.comment
    - audits.created_at
    - commit_statuses.id
    - commit_statuses.commit_sha
    - commit_statuses.status
    - commit_statuses.commit_message
    - commit_statuses.created_at
    - commit_statuses.updated_at
    - commit_statuses.ci_url
    - deletion_logs.id
    - deletion_logs.post_id
    - deletion_logs.is_deleted
    - deletion_logs.created_at
    - deletion_logs.updated_at
    - deletion_logs.api_key_id
    - domain_tags.id
    - domain_tags.name
    - domain_tags.description
    - domain_tags.created_at
    - domain_tags.updated_at
    - feedbacks.id
    - feedbacks.message_link
    - feedbacks.user_name
    - feedbacks.user_link
    - feedbacks.feedback_type
    - feedbacks.post_id
    - feedbacks.post_link
    - feedbacks.user_id
    - feedbacks.is_invalidated
    - feedbacks.invalidated_by
    - feedbacks.invalidated_at
    - feedbacks.chat_user_id
    - feedbacks.created_at
    - feedbacks.updated_at
    - feedbacks.api_key_id
    - feedbacks.chat_host
    - flag_conditions.id
    - flag_conditions.flags_enabled
    - flag_conditions.min_weight
    - flag_conditions.max_poster_rep
    - flag_conditions.min_reason_count
    - flag_conditions.user_id
    - flag_conditions.created_at
    - flag_conditions.updated_at
    - flag_logs.id
    - flag_logs.success
    - flag_logs.error_message
    - flag_logs.flag_condition_id
    - flag_logs.user_id
    - flag_logs.post_id
    - flag_logs.created_at
    - flag_logs.updated_at
    - flag_logs.is_dry_run
    - flag_logs.backoff
    - flag_logs.site_id
    - flag_logs.is_auto
    - flag_logs.api_key_id
    - flag_settings.id
    - flag_settings.name
    - flag_settings.value
    - flag_settings.created_at
    - flag_settings.updated_at
    - moderator_sites.id
    - moderator_sites.user_id
    - moderator_sites.site_id
    - moderator_sites.created_at
    - moderator_sites.updated_at
    - posts.id
    - posts.title
    - posts.body
    - posts.link
    - posts.post_creation_date
    - posts.created_at
    - posts.updated_at
    - posts.site_id
    - posts.user_link
    - posts.username
    - posts.why
    - posts.user_reputation
    - posts.score
    - posts.upvote_count
    - posts.downvote_count
    - posts.stack_exchange_user_id
    - posts.is_tp
    - posts.is_fp
    - posts.is_naa
    - posts.revision_count
    - posts.deleted_at
    - posts.smoke_detector_id
    - posts.autoflagged
    - reasons.id
    - reasons.reason_name
    - reasons.last_post_title
    - reasons.inactive
    - reasons.weight
    - reasons.maximum_weight
    - roles.id
    - roles.name
    - roles.resource_type
    - roles.resource_id
    - roles.created_at
    - roles.updated_at
    - sites.id
    - sites.site_name
    - sites.site_url
    - sites.site_logo
    - sites.site_domain
    - sites.created_at
    - sites.updated_at
    - sites.flags_enabled
    - sites.max_flags_per_post
    - sites.is_child_meta
    - sites.last_users_update
    - smoke_detectors.id
    - smoke_detectors.last_ping
    - smoke_detectors.name
    - smoke_detectors.location
    - smoke_detectors.created_at
    - smoke_detectors.updated_at
    - smoke_detectors.email_date
    - smoke_detectors.user_id
    - smoke_detectors.is_standby
    - smoke_detectors.force_failover
    - spam_domains.id
    - spam_domains.domain
    - spam_domains.whois
    - spam_domains.created_at
    - spam_domains.updated_at
    - stack_exchange_users.id
    - stack_exchange_users.user_id
    - stack_exchange_users.username
    - stack_exchange_users.last_api_update
    - stack_exchange_users.still_alive
    - stack_exchange_users.answer_count
    - stack_exchange_users.question_count
    - stack_exchange_users.reputation
    - stack_exchange_users.created_at
    - stack_exchange_users.updated_at
    - stack_exchange_users.site_id
    - statistics.id
    - statistics.posts_scanned
    - statistics.smoke_detector_id
    - statistics.created_at
    - statistics.updated_at
    - statistics.api_quota
    - statistics.post_scan_rate
    - user_site_settings.id
    - user_site_settings.max_flags
    - user_site_settings.flags_used
    - user_site_settings.user_id
    - user_site_settings.created_at
    - user_site_settings.updated_at
    - users.id
    - users.remember_created_at
    - users.created_at
    - users.updated_at
    - users.username
    - users.stackexchange_chat_id
    - users.meta_stackexchange_chat_id
    - users.stackoverflow_chat_id
    - users.stack_exchange_account_id
    - users.flags_enabled
    - users.enabled_2fa
    - users.announcement_emails
    - users.oauth_created
    - post_comments.id
    - post_comments.post_id
    - post_comments.user_id
    - post_comments.text
    - post_comments.created_at
    - post_comments.updated_at
  sensitive_fields:
    - api_keys.key
    - api_tokens.code
    - api_tokens.token
    - audits.remote_address
    - audits.request_uuid
    - emails_addressees.name
    - emails_addressees.email_address
    - emails_addressees.manage_key
    - emails_preferences.consent_via
    - emails_preferences.consent_comment
    - flags.reason
    - flags.user_id
    - github_tokens.token
    - ignored_users.user_name
    - ignored_users.user_id
    - smoke_detectors.access_token
    - users.email
    - users.encrypted_password
    - users.reset_password_token
    - users.reset_password_sent_at
    - users.encrypted_api_token
    - users.two_factor_token
    - users.salt
    - users.iv

development:
  <<: *common

test:
  <<: *common
  redis_logging:
    adapter: redis
    timeout: 200
    url: redis://localhost:6379

production:
  <<: *common


text:
# SQLite version 3.x
#   gem install sqlite3
#
#   Ensure the SQLite 3 gem is defined in your Gemfile
#   gem 'sqlite3'
#
default: &default
  adapter: sqlite3
  pool: 5
  timeout: 5000

development:
  <<: *default
  database: db/development.sqlite3

# Warning: The database defined as "test" will be erased and
# re-generated from your development database when you run "rake".
# Do not set this db to the same as development or production.
test:
  <<: *default
  database: db/test.sqlite3

production:
  <<: *default
  database: db/production.sqlite3


text:
# Note: You must restart bin/webpack-dev-server for changes to take effect

default: &default
  source_path: app/javascript
  source_entry_path: packs
  public_output_path: packs

  extensions:
    - .coffee
    - .erb
    - .js
    - .jsx
    - .ts
    - .vue
    - .sass
    - .scss
    - .css
    - .png
    - .svg
    - .gif
    - .jpeg
    - .jpg

development:
  <<: *default

  dev_server:
    host: 127.0.0.1
    port: 8080
    https: false

test:
  <<: *default

  public_output_path: packs-test

production:
  <<: *default


text:
# Be sure to restart your server when you modify this file.

# Your secret key is used for verifying the integrity of signed cookies.
# If you change this key, all old signed cookies will become invalid!

# Make sure the secret is at least 30 characters and all random,
# no regular words or you'll be exposed to dictionary attacks.
# You can use `rake secret` to generate a secure secret key.

# Make sure the secrets in this file are kept private
# if you're sharing your code publicly.

development:
  secret_key_base: e4e636fe1d34a129331e081d37bf5e26547d2ee619090f01592be8ec179b18bb027981cb957227fa624d667aa72c4058206bcfb2095950fc9a1fdd86852d3fc3

test:
  secret_key_base: bf0b3bdd1cb7dd82f9bcff77db4eeb60610105f8199717cc58b21c7689c6d9a55a1fc43b0bcec5681bb4f7d207f524d324bf4fd340ad86f130f5d395e9f3fa9f

# Do not keep production secrets in the repository,
# instead read values from the environment.
production:
  secret_key_base: 48e389ba1c1d36676c1c006228a7108e05fe09ed1c1a3215a0f1c1a5c0b3f91b2c387b1603d8476cc5c42a456910ba4884e5907cc839183616178a60aff4a978


text:
# frozen_string_literal: true

require File.expand_path('../boot', __FILE__)

require 'rails/all'

# Require the gems listed in Gemfile, including any gems
# you've limited to :test, :development, or :production.
Bundler.require(*Rails.groups)

module Metasmoke
  class Application < Rails::Application
    # Settings in config/environments/* take precedence over those specified here.
    # Application configuration should go into files in config/initializers
    # -- all .rb files in that directory are automatically loaded.

    # Set Time.zone default to the specified zone and make Active Record auto-convert to this zone.
    # Run 'rake -D time' for a list of tasks for finding time zone names. Default is UTC.
    # config.time_zone = 'Central Time (US & Canada)'

    # The default locale is :en and all translations from config/locales/*.rb,yml are auto loaded.
    # config.i18n.load_path += Dir[Rails.root.join('my', 'locales', '*.{rb,yml}').to_s]
    # config.i18n.default_locale = :de

    config.middleware.insert_before 0, Rack::Cors do
      allow do
        origins '*'
        # 'http://stackoverflow.com', 'http://superuser.com', 'http://serverfault.com',
        # /^http:\/\/.*.stackexchange.com$/, 'https://stackoverflow.com', 'https://superuser.com',
        # 'https://serverfault.com', /^https:\/\/.*.stackexchange.com$/

        resource '/posts/recent.json', headers: :any, methods: [:get]
        resource '/posts/add_feedback', headers: :any, methods: [:post], credentials: true

        resource '/api/*', headers: :any, methods: %i[get post]
        resource '/api/w/*', headers: :any, methods: [:post], credentials: true

        resource '/oauth/token', headers: :any, methods: [:get]
      end
    end

    config.after_initialize do
      # Only authorize MiniProfiler if none of the blacklisted pp modes are specified.
      Rack::MiniProfiler.config.authorization_mode = :whitelist
      Rack::MiniProfiler.config.position = 'top-right'
    end
  end
end

AppConfig = YAML.load_file("#{Rails.root}/config/config.yml")[Rails.env]


text:
# frozen_string_literal: true

# routes.rb style guidance:
#
# - Root at the top
# - Unscoped routes next; group them logically
# - Throw everything else in a scope, alphabetically ordered.
# - Within a scope, unscoped routes go _after_ everything else.
# - Unscoped mounts at the bottom.
#
# RubyMine hint: Within the draw block, Ctrl-Shift-Minus followed by Ctrl-= (or cmd, whatever system you're on)
# will collapse everything and then expand the top level again so you just see unscoped routes plus scopes. Makes
# it much easier to see where everything is.
Rails.application.routes.draw do
  # Needs both with and without :as, ref Charcoal-SE/metasmoke#247
  root               to: 'dashboard#new_dash',  as: :dashboard
  root               to: 'dashboard#new_dash'

  resources :dumps

  get    'sites/dash',            to: 'dashboard#site_dash',        as: :site_dash

  get    'search',                to: 'search#index'
  get    'search_fast',           to: 'search#index_fast'
  get    'reasons',               to: 'dashboard#index',            as: :reasons
  get    'flagging',              to: 'flag_settings#dashboard',    as: :flagging

  get    'query-times',           to: 'dashboard#query_times',      as: :query_times
  post   'query-times/reset',     to: 'dashboard#reset_query_time', as: :reset_query_time

  post   'statistics.json',       to: 'statistics#create'
  post   'feedbacks.json',        to: 'feedbacks#create'
  post   'posts.json',            to: 'posts#create'
  post   'deletion_logs.json',    to: 'deletion_logs#create'
  post   'status-update.json',    to: 'status#status_update'

  delete 'feedback/:id/delete',   to: 'feedbacks#delete',           as: :delete_feedback

  devise_for :users, controllers: { sessions: 'custom_sessions', registrations: 'custom_registrations' }
  devise_scope :user do
    get  'users/2fa/login', to: 'custom_sessions#verify_2fa'
    post 'users/2fa/login', to: 'custom_sessions#verify_code'
  end

  scope 'abuse' do
    scope 'contacts' do
      root               to: 'abuse_contacts#index',   as: :abuse_contacts
      post   'new',      to: 'abuse_contacts#create',  as: :create_abuse_contact
      get    ':id',      to: 'abuse_contacts#show',    as: :abuse_contact
      get    ':id/edit', to: 'abuse_contacts#edit',    as: :edit_abuse_contact
      patch  ':id/edit', to: 'abuse_contacts#update',  as: :update_abuse_contact
      delete ':id',      to: 'abuse_contacts#destroy', as: :destroy_abuse_contact
    end

    scope 'statuses' do
      root               to: 'abuse_report_statuses#index',   as: :abuse_statuses
      post   'new',      to: 'abuse_report_statuses#create',  as: :create_abuse_status
      get    ':id',      to: 'abuse_report_statuses#show',    as: :abuse_status
      get    ':id/edit', to: 'abuse_report_statuses#edit',    as: :edit_abuse_status
      patch  ':id/edit', to: 'abuse_report_statuses#update',  as: :update_abuse_status
      delete ':id',      to: 'abuse_report_statuses#destroy', as: :destroy_abuse_status
    end

    scope 'reports' do
      root                   to: 'abuse_reports#index',         as: :abuse_reports
      get    'new',          to: 'abuse_reports#new',           as: :new_abuse_report
      post   'new',          to: 'abuse_reports#create',        as: :create_abuse_report
      get    'shared/:uuid', to: 'abuse_reports#public_link',   as: :shared_abuse_report
      get    ':id',          to: 'abuse_reports#show',          as: :abuse_report
      patch  ':id/edit',     to: 'abuse_reports#update',        as: :update_abuse_report
      post   ':id/status',   to: 'abuse_reports#update_status', as: :update_abuse_report_status
      delete ':id',          to: 'abuse_reports#destroy',       as: :destroy_abuse_report
    end

    scope 'comments' do
      post   'new',        to: 'abuse_comments#create',  as: :create_abuse_comment
      get    ':id',        to: 'abuse_comments#text',    as: :comment_abuse_text
      post   ':id/edit',   to: 'abuse_comments#update',  as: :edit_abuse_comment
      delete ':id/delete', to: 'abuse_comments#destroy', as: :delete_abuse_comment
    end
  end

  scope 'admin' do
    scope 'settings' do
      root                 to: 'site_settings#index',   as: :site_settings
      post   ':name',      to: 'site_settings#update',  as: :update_site_setting
      delete ':id/delete', to: 'site_settings#destroy', as: :destroy_site_setting
    end

    root                           to: 'admin#index'
    get    'invalidated',          to: 'admin#recently_invalidated'
    get    'user_feedback',        to: 'admin#user_feedback',               as: :admin_user_feedback
    get    'api_feedback',         to: 'admin#api_feedback'
    get    'flagged',              to: 'admin#flagged'
    post   'clear_flag',           to: 'admin#clear_flag'
    get    'users',                to: 'admin#users',                       as: :user_data
    get    'permissions',          to: 'admin#permissions',                 as: :admin_permissions
    put    'permissions/update',   to: 'admin#update_permissions'
    delete 'permissions/:user_id', to: 'admin#destroy_user'

    get    'invalidate_tokens',    to: 'authentication#invalidate_tokens'
    post   'invalidate_tokens',    to: 'authentication#send_invalidations'

    get    'new_key',              to: 'api_keys#new',                      as: :admin_new_key
    post   'new_key',              to: 'api_keys#create'
    get    'keys',                 to: 'api_keys#index'
    get    'keys/mine',            to: 'api_keys#mine'
    get    'edit_key/:id',         to: 'api_keys#edit'
    patch  'edit_key/:id',         to: 'api_keys#update'
    get    'owner_edit/:id',       to: 'api_keys#owner_edit'
    patch  'owner_edit/:id',       to: 'api_keys#owner_update'
    delete 'revoke_write',         to: 'api_keys#revoke_write_tokens'
    delete 'owner_revoke',         to: 'api_keys#owner_revoke'
    post   'keys/:id/trust',       to: 'api_keys#update_trust'
  end

  scope 'announcements' do
    root               to: 'announcements#index',  as: :announcements
    post ':id/expire', to: 'announcements#expire', as: :announcements_expire
    get  'new',        to: 'announcements#new'
    post 'new',        to: 'announcements#create'
    post 'enable',     to: 'users#set_announcement_emails'
  end

  scope 'api' do
    root                            to: 'api#api_docs',              as: :api_docs
    get  'filters',                 to: 'api#filter_generator'
    post 'filters',                 to: 'api#calculate_filter'

    get  'filter_fields',           to: 'api#filter_fields'
    get  'stats/last_week',         to: 'api#spam_last_week'
    get  'stats/detailed_ttd',      to: 'api#detailed_ttd'
    get  'smoke_detectors/status',  to: 'api#current_status'
    get  'smoke_detectors',         to: 'api#smoke_detectors'
    get  'posts/urls',              to: 'api#posts_by_url'
    post 'posts/urls',              to: 'api#posts_by_url'
    get  'posts/feedback',          to: 'api#posts_by_feedback'
    get  'posts/undeleted',         to: 'api#undeleted_posts'
    get  'posts/site',              to: 'api#posts_by_site'
    get  'posts/between',           to: 'api#posts_by_daterange'
    get  'posts/search',            to: 'api#search_posts'
    get  'posts/:ids',              to: 'api#posts'
    get  'post/:id/feedback',       to: 'api#post_feedback'
    get  'post/:id/reasons',        to: 'api#post_reasons'
    get  'post/:id/valid_feedback', to: 'api#post_valid_feedback'
    get  'reasons',                 to: 'api#all_reasons'
    get  'reasons/:ids',            to: 'api#reasons'
    get  'reason/:id/posts',        to: 'api#reason_posts'
    get  'blacklist',               to: 'api#blacklisted_websites'
    get  'users',                   to: 'api#users',                 as: :api_users
    get  'users/code_privileged',   to: 'api#users_with_code_privs'
    get  'post/:id/domains',        to: 'api#post_domains',          as: :api_post_domains
    get  'domains/:id/tags',        to: 'api#domain_tags',           as: :api_domain_tags

    post 'w/post/:id/feedback',     to: 'api#create_feedback'
    post 'w/post/report',           to: 'api#report_post'
    post 'w/post/:id/spam_flag',    to: 'api#spam_flag'
    post 'w/post/:id/deleted',      to: 'api#post_deleted'
    post 'w/domains/:id/add_tag',   to: 'api#add_domain_tag',        as: :api_add_domain_tag

    post 'graphql',                 to: 'graphql#execute',           as: :graphql
    get 'graphql',                  to: 'graphql#query',             as: :query_graphql
  end
  mount GraphiQL::Rails::Engine,  at: '/api/graphiql',                 graphql_path: '/api/graphql', query_params: true

  scope 'authentication' do
    get 'status',                to: 'authentication#status', as: :authentication_status
    get 'redirect_target',       to: 'authentication#redirect_target'
    get 'login_redirect_target', to: 'authentication#login_redirect_target'
  end

  scope 'channels' do
    post 'receive_email', to: 'channels#receive_email',     as: :channels_receive_email
    get  'email',         to: 'channels#get_email_address', as: :channels_email
    get  'link',          to: 'channels#show_link',         as: :channels_link
  end

  scope 'comments' do
    post 'new',          to: 'post_comments#create',  as: :create_comment
    get  ':id',          to: 'post_comments#text',    as: :comment_text
    post ':id/edit',     to: 'post_comments#update',  as: :edit_comment
    delete ':id/delete', to: 'post_comments#destroy', as: :delete_comment
  end

  scope 'data' do
    authenticate(:user, ->(user) { user.has_role?(:core) }) do
      mount Blazer::Engine, at: 'sql'
    end
  end

  scope 'dev' do
    post 'update_sites',     to: 'developer#update_sites',          as: :dev_update_sites
    get  'prod_log',         to: 'developer#production_log',        as: :dev_prod_log
    post 'prod_log_search',  to: 'developer#production_log_search', as: :dev_prod_log_search
    get  'query_time_log',   to: 'developer#query_times_log',       as: :dev_query_times_log
    get  'blank',            to: 'developer#blank_page',            as: :dev_blank
    get  'layout',           to: 'developer#empty_layout',          as: :dev_layout
    get  'websockets',       to: 'developer#websocket_test'
    post 'websockets',       to: 'developer#send_websocket_test'
    post 'deploy',           to: 'developer#deploy',                as: :developer_deploy
    get  'impersonate/stop', to: 'developer#change_back',           as: :stop_impersonating
    post 'impersonate/stop', to: 'developer#verify_elevation',      as: :verify_elevation
    post 'impersonate/:id',  to: 'developer#change_users',          as: :impersonate
    post 'fcrs',             to: 'developer#run_fcrs',              as: :developer_fcrs
    post 'reindex',          to: 'developer#run_feedback_reindex',  as: :developer_reindex

    scope 'request-log' do
      root                  to: 'redis_log#index'
      get 'user/:id',       to: 'redis_log#by_user',    as: :redis_log_by_user
      get 'status/:status', to: 'redis_log#by_status',  as: :redis_log_by_status
      get 'by_path/:method/:path.:format', to: 'redis_log#by_path',  as: :redis_log_by_path, constraints: { path: /.+/ }
      get 'session/:id',    to: 'redis_log#by_session', as: :redis_log_by_session
      scope 'request/:timestamp/:request_id', constraints: {:timestamp => /[^\/]+/ } do
        root to: 'redis_log#show', as: :redis_log_request
        get 'save', to: 'redis_log#save', as: :redis_log_save_request
        get 'unsave', to: 'redis_log#unsave', as: :redis_log_unsave_request
      end
    end
  end

  scope 'domains' do
    scope 'groups' do
      root               to: 'domain_groups#index',   as: :domain_groups
      get    'new',      to: 'domain_groups#new',     as: :new_domain_group
      post   'new',      to: 'domain_groups#create',  as: :create_domain_group
      get    ':id',      to: 'domain_groups#show',    as: :domain_group
      post   ':id/mail', to: 'domain_groups#emails',  as: :domain_group_emails
      get    ':id/edit', to: 'domain_groups#edit',    as: :edit_domain_group
      patch  ':id/edit', to: 'domain_groups#update',  as: :update_domain_group
      delete ':id',      to: 'domain_groups#destroy', as: :destroy_domain_group
    end

    scope 'tags' do
      root                            to: 'domain_tags#index',           as: :domain_tags
      post   'add',                   to: 'domain_tags#add',             as: :add_domain_tag
      post   'add_post',              to: 'domain_tags#add_post',        as: :add_post_tag
      post   'add_review',            to: 'domain_tags#add_review',      as: :review_add_tag
      post   'remove',                to: 'domain_tags#remove',          as: :remove_domain_tag
      post   'remove_post',           to: 'domain_tags#remove_post',     as: :remove_post_tag
      get    'mass',                  to: 'domain_tags#mass_tagging',    as: :domain_tags_mass_tagging
      post   'mass',                  to: 'domain_tags#submit_mass_tag', as: :domain_tags_submit_tagging
      post   'merge',                 to: 'domain_tags#merge',           as: :merge_tags
      get    ':id/edit',              to: 'domain_tags#edit',            as: :edit_domain_tag
      patch  ':id/edit',              to: 'domain_tags#update',          as: :update_domain_tag
      get    ':id',                   to: 'domain_tags#show',            as: :domain_tag
      delete ':id',                   to: 'domain_tags#destroy',         as: :destroy_domain_tag
    end

    scope 'links' do
      post   'create',    to: 'domain_links#create',  as: :create_domain_link
      patch  'update',    to: 'domain_links#update',  as: :update_domain_link
      delete ':id',       to: 'domain_links#destroy', as: :destroy_domain_link
    end

    root                     to: 'spam_domains#index',             as: :spam_domains
    get    'new',            to: 'spam_domains#new',               as: :new_spam_domain
    post   'create.json',    to: 'spam_domains#create_from_post',  as: :create_spam_domain
    post   'no_post_create', to: 'spam_domains#create',            as: :create_no_post_spam_domain
    get    'query.json',     to: 'spam_domains#query',             as: :spam_domains_query
    post   ':id/fix_asn',    to: 'spam_domains#fix_asn_tags',       as: :fix_asn_tags
    get    ':id/edit',       to: 'spam_domains#edit',              as: :edit_spam_domain
    patch  ':id/edit',       to: 'spam_domains#update',            as: :update_spam_domain
    get    ':id',            to: 'spam_domains#show',              as: :spam_domain
    delete ':id',            to: 'spam_domains#destroy',           as: :destroy_spam_domain
  end

  scope 'email' do
    scope 'preferences' do
      root                  to: 'emails/preferences#search',    as: :email_preferences_search
      get  'show',          to: 'emails/preferences#list',      as: :email_preferences_list
      post ':id/toggle',    to: 'emails/preferences#toggle',    as: :toggle_email_preference
      post ':id/frequency', to: 'emails/preferences#frequency', as: :change_email_frequency
      post ':id/delete',    to: 'emails/preferences#destroy',   as: :destroy_email_preference
    end
  end

  scope 'flagging' do
    scope 'audits' do
      get 'settings', to: 'flag_settings#audits', as: 'flag_settings_audits'
    end

    resources :flag_settings,         path: 'settings',    except: [:show]
    resources :flag_conditions,       path: 'conditions',  except: [:show]
    resources :user_site_settings,    path: 'preferences', except: [:show]

    get   'settings/sites',           to: 'flag_settings#site_settings',        as: :flagging_site_settings
    post  'settings/sites',           to: 'flag_settings#update_site_settings', as: :update_flagging_site_settings

    post  'smokey_disable',           to: 'flag_settings#smokey_disable_flagging'

    get   'by-site',                  to: 'flag_settings#by_site',              as: :flagging_by_site

    get   'ocs',                      to: 'flag_conditions#one_click_setup'
    post  'run_ocs',                  to: 'flag_conditions#run_ocs'

    get   'conditions/all',           to: 'flag_conditions#full_list'
    get   'conditions/preview',       to: 'flag_conditions#preview'
    get   'conditions/sandbox',       to: 'flag_conditions#sandbox'

    patch 'conditions/:id/enable',    to: 'flag_conditions#enable',             as: :flag_conditions_enable
    post  'conditions/validate_user', to: 'flag_conditions#validate_user',      as: :flag_conditions_validate_user

    get   'preferences/user/:user',   to: 'user_site_settings#for_user'
    post  'preferences/enable',       to: 'user_site_settings#enable_flagging'

    get   'logs',                     to: 'flag_log#index',                     as: :flag_logs
    get   'logs/unflagged',           to: 'flag_log#not_flagged',               as: :unflagged_logs
    get   'users/:user_id/logs',      to: 'flag_log#index',                     as: :flag_logs_by_user
    get   'users/overview',           to: 'flag_conditions#user_overview',      as: :user_overview
    get   'users',                    to: 'users#flagging_enabled',             as: :flagging_users
  end

  scope 'github' do
    post 'status_hook',                 to: 'github#status_hook',                 as: :github_status_hook
    post 'pull_request_hook',           to: 'github#pull_request_hook',           as: :github_pull_request_hook
    post 'ci_hook',                     to: 'github#ci_hook',                     as: :github_ci_hook
    post 'update_deploy_to_master',     to: 'github#update_deploy_to_master',     as: :github_update_deploy_to_master
    post 'metasmoke_push_hook',         to: 'github#metasmoke_push_hook',         as: :github_metasmoke_push_hook
    post 'gollum',                      to: 'github#gollum_hook',                 as: :github_gollum_hook
    post 'project_status',              to: 'github#any_status_hook',             as: :github_project_status_hook
    post 'report_check_run_failure',    to: 'github#report_check_run_failure',    as: :github_report_check_run_failure
    post 'report_check_suite_success',  to: 'github#report_check_suite_success',  as: :github_report_check_suite_success
    post 'pr_merge',                    to: 'github#pullapprove_merge_hook',      as: :github_pr_merge_hook
    post 'pr_approve/:number',          to: 'github#add_pullapprove_comment',     as: :github_pr_approve_comment
  end

  scope 'graphs' do
    root                     to: 'graphs#index',                  as: :graphs
    get 'flagging_results',  to: 'graphs#flagging_results',       as: :flagging_results_graph
    get 'flagging_timeline', to: 'graphs#flagging_timeline',      as: :flagging_timeline_graph
    get 'reports_hours',     to: 'graphs#reports_by_hour',        as: :reports_by_hour_graph
    get 'reports_sites',     to: 'graphs#reports_by_site'
    get 'reports_hod',       to: 'graphs#reports_by_hour_of_day'
    get 'ttd',               to: 'graphs#time_to_deletion'
    get 'dttd',              to: 'graphs#detailed_ttd'
    get 'report_counts',     to: 'graphs#report_counts',          as: :report_counts_graph
    get 'reason_counts',     to: 'graphs#reason_counts',          as: :reason_counts_graph
    get 'monthly_ttd',       to: 'graphs#monthly_ttd',            as: :monthly_ttd_graph
    get 'reports',           to: 'graphs#reports',                as: :reports_graph
    get 'af_accuracy',       to: 'graphs#af_accuracy',            as: :af_accuracy
    get 'timings/by_action/:controller_name/:action_name', to: 'graphs#query_times_graphs', as: :query_times_graph
    get 'qtimes/:controller_name/:action_name', to: 'graphs#qtimes'
  end

  scope 'magic' do
    get 'funride', to: 'dashboard#funride'
  end

  scope 'oauth' do
    get 'request',     to: 'micro_auth#token_request', as: :oauth_request
    post 'authorize',  to: 'micro_auth#authorize',     as: :oauth_authorize
    get 'authorized',  to: 'micro_auth#authorized',    as: :oauth_authorized
    get 'reject',      to: 'micro_auth#reject',        as: :oauth_reject
    get 'token',       to: 'micro_auth#token',         as: :oauth_token
    get 'invalid_key', to: 'micro_auth#invalid_key',   as: :oauth_invalid_key
  end

  scope 'post' do
    get  ':id',                   to: 'posts#show',                 as: :post
    get  ':id/body',              to: 'posts#body'
    get  ':id/feedbacks.json',    to: 'posts#feedbacksapi'
    get  ':id/flag_logs',         to: 'flag_log#by_post',           as: :post_flag_logs
    get  ':id/eligible_flaggers', to: 'flag_log#eligible_flaggers', as: :post_eligible_flaggers
    post ':id/index_feedback',    to: 'posts#reindex_feedback'
    post ':id/spam_flag',         to: 'posts#cast_spam_flag'
    post ':id/delete',            to: 'posts#delete_post',          as: :dev_delete_post
    post ':post_id/feedback',     to: 'posts#feedback',             as: :post_feedback
    get  ':id/feedback/clear',    to: 'feedbacks#clear',            as: :clear_post_feedback
    post ':id/admin_flag',        to: 'posts#needs_admin',          as: :admin_flag_post
    post ':id/domains/add',       to: 'posts#add_domain',           as: :add_post_domain
    post ':id/domains/remove',    to: 'posts#remove_domain',        as: :remove_post_domain
  end

  scope 'posts' do
    root                              to: 'posts#index',            as: :posts
    get  'latest',                    to: 'posts#latest'
    get  'by-url',                    to: 'posts#by_url'
    get  'uid/:api_param/:native_id', to: 'posts#by_uid',           constraints: { api_param: %r{[^\/]+} }
    get  'by-site',                   to: 'dashboard#spam_by_site', as: :spam_by_site
    get  'recent.json',               to: 'posts#recentpostsapi'
    post 'add_feedback',              to: 'review#add_feedback'
  end

  scope 'privacy' do
    root              to: 'dashboard#privacy',         as: :data_privacy
    get 'processing', to: 'dashboard#data_processing', as: :data_processing
  end

  scope 'reason' do
    post 'description',        to: 'reasons#update_description', as: :update_reason_description
    get  ':id',                to: 'reasons#show',               as: :reason
    get  ':id/site_chart',     to: 'reasons#sites_chart',        as: :reason_site_chart
    get  ':id/accuracy_chart', to: 'reasons#accuracy_chart',     as: :reason_accuracy_chart
  end

  scope 'review' do
    root                     to: 'review_queues#index',         as: :review_queues
    get    ':name',          to: 'review_queues#queue',         as: :review_queue
    get    ':name/next',     to: 'review_queues#next_item',     as: :next_review_item
    get    ':name/history',  to: 'review_queues#reviews',       as: :review_history
    post   ':name/recheck',  to: 'review_queues#recheck_items', as: :recheck_queue_items
    get    ':name/:item_id', to: 'review_queues#item',          as: :review_item
    post   ':name/:item_id', to: 'review_queues#submit',        as: :submit_review
    delete ':name/:id',      to: 'review_queues#delete',        as: :delete_review
  end

  scope 'rss' do
    get 'v1/deleted', to: 'rss#deleted', as: :rss
  end

  scope 'smoke_detector' do
    get    'mine',               to: 'smoke_detectors#mine',           as: :smoke_detector_mine
    get    'new',                to: 'smoke_detectors#new',            as: :smoke_detector_new
    post   'create',             to: 'smoke_detectors#create',         as: :smoke_detector_create
    post   ':id/token_regen',    to: 'smoke_detectors#token_regen',    as: :smoke_detector_token_regen
    get    ':id/statistics',     to: 'statistics#index',               as: :smoke_detector_statistics
    delete ':id',                to: 'smoke_detectors#destroy',        as: :smoke_detector_delete
    post   ':id/force_failover', to: 'smoke_detectors#force_failover', as: :smoke_detector_force_failover
    post   ':id/force_pull',     to: 'smoke_detectors#force_pull',     as: :smoke_detector_force_pull
    get    'audits',             to: 'smoke_detectors#audits'
    get    'check_token/:token', to: 'smoke_detectors#check_token'
  end

  scope 'spammers' do
    root                      to: 'stack_exchange_users#index'
    get  'sites',             to: 'stack_exchange_users#sites',       as: :spammers_site_index
    get  'site',              to: 'stack_exchange_users#on_site',     as: :spammers_on_site
    post 'site/:site/update', to: 'stack_exchange_users#update_data'
    post 'dead/:id',          to: 'stack_exchange_users#dead'
    get  ':id',               to: 'stack_exchange_users#show',        as: :stack_exchange_user
  end

  scope 'spam-waves' do
    root                 to: 'spam_waves#index',   as: :spam_waves
    get    'new',        to: 'spam_waves#new',     as: :new_spam_wave
    post   'new',        to: 'spam_waves#create',  as: :create_spam_wave
    get    'preview',    to: 'spam_waves#preview', as: :preview_spam_wave
    get    ':id',        to: 'spam_waves#show',    as: :spam_wave
    get    ':id/edit',   to: 'spam_waves#edit',    as: :edit_spam_wave
    post   ':id/edit',   to: 'spam_waves#update',  as: :update_spam_wave
    post   ':id/cancel', to: 'spam_waves#cancel',  as: :cancel_spam_wave
    post   ':id/renew',  to: 'spam_waves#renew',   as: :renew_spam_wave
  end

  scope 'status' do
    root                     to: 'status#index',      as: :status
    get  'code.json', to: 'code_status#api'
    get  'code',      to: 'code_status#index', as: :code_status
    post 'kill',      to: 'status#kill',       as: :kill_smokey
  end

  scope 'users' do
    scope '2fa' do
      root                 to: 'users#tf_status'
      post 'enable',       to: 'users#enable_2fa'
      get 'enable/code',   to: 'users#enable_code'
      post 'enable/code',  to: 'users#confirm_enable_code'
      get 'disable/code',  to: 'users#disable_code'
      post 'disable/code', to: 'users#confirm_disable_code'
    end

    root                           to: 'admin#users',               as: :users
    get    'username',             to: 'users#username',            as: :users_username
    post   'username',             to: 'users#set_username',        as: :set_username
    get    'apps',                 to: 'users#apps',                as: :users_apps
    delete 'revoke_app',           to: 'users#revoke_app',          as: :users_revoke_app

    post   'update_email',         to: 'users#update_email'

    get    'denied',               to: 'users#missing_privileges',  as: :missing_privileges

    get    'migrate_token',        to: 'users#migrate_token_confirmation', as: :migrate_token_confirmation
    post   'migrate_token',        to: 'users#migrate_token',        as: :migrate_token

    get    ':id',                  to: 'users#show',                as: :dev_user, constraints: { id: /-?\d+/ }
    post   ':id/update_ids',       to: 'users#refresh_ids',         as: :update_user_chat_ids
    post   ':id/reset_pass',       to: 'users#send_password_reset', as: :send_password_reset
    post   ':id/update_mod_sites', to: 'users#update_mod_sites',    as: :update_mod_sites
  end

  # This should always be right at the end of this file, so that it doesn't override other routes.
  mount API::Base => '/api'
  mount ActionCable.server => '/cable'
end


text:
# frozen_string_literal: true

# Load the Rails application.
require File.expand_path('../application', __FILE__)

# Initialize the Rails application.
Rails.application.initialize!


text:
# frozen_string_literal: true

ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', __FILE__)

require 'bundler/setup' # Set up gems listed in the Gemfile.


text:
# frozen_string_literal: true

set :application, 'metasmoke'
set :repo_url, 'https://github.com/Charcoal-SE/metasmoke.git'

# Default branch is :master
set :branch, `git rev-parse --abbrev-ref HEAD`.chomp

# Default deploy_to directory is /var/www/my_app_name
set :deploy_to, '/var/railsapps/metasmoke'

# Default value for :scm is :git
# set :scm, :git

# Default value for :format is :pretty
# set :format, :pretty

# Default value for :log_level is :debug
# set :log_level, :debug

# Default value for :pty is false
# set :pty, true

# Default value for :linked_files is []
set :linked_files, fetch(:linked_files, ['config/database.yml']).push(
  'config/database.yml',
  'config/secrets.yml',
  'config/config.yml',
  'config/blazer.yml',
  'config/cable.yml',
  'config/ms-ci.pem'
)

# Default value for linked_dirs is []
set :linked_dirs, fetch(:linked_dirs, []).push(
  'log',
  'node_modules',
  'dumps'
)

# Default value for default_env is {}
# set :default_env, { path: '/opt/ruby/bin:$PATH' }

# Default value for keep_releases is 5
# set :keep_releases, 5

namespace :deploy do
  after :restart, :clear_cache do
    on roles(:web), in: :groups, limit: 3, wait: 10 do
      # Here we can do anything such as:
      # within release_path do
      #   execute :rake, 'cache:clear'
      # end
    end
  end
end


text:
test:
  service: Disk
  root: <%= Rails.root.join("tmp/storage") %>

local:
  service: Disk
  root: <%= Rails.root.join("storage") %>

# Use rails credentials:edit to set the AWS secrets (as aws:access_key_id|secret_access_key)
# amazon:
#   service: S3
#   access_key_id: <%= Rails.application.credentials.dig(:aws, :access_key_id) %>
#   secret_access_key: <%= Rails.application.credentials.dig(:aws, :secret_access_key) %>
#   region: us-east-1
#   bucket: your_own_bucket

# Remember not to checkin your GCS keyfile to a repository
# google:
#   service: GCS
#   project: your_project
#   credentials: <%= Rails.root.join("path/to/gcs.keyfile") %>
#   bucket: your_own_bucket

# Use rails credentials:edit to set the Azure Storage secret (as azure_storage:storage_access_key)
# microsoft:
#   service: AzureStorage
#   storage_account_name: your_account_name
#   storage_access_key: <%= Rails.application.credentials.dig(:azure_storage, :storage_access_key) %>
#   container: your_container_name

# mirror:
#   service: Mirror
#   primary: local
#   mirrors: [ amazon, google, microsoft ]


text:
# frozen_string_literal: true

Spring.after_fork do
  if ENV['DEBUGGER_STORED_RUBYLIB']
    ENV['DEBUGGER_STORED_RUBYLIB'].split(File::PATH_SEPARATOR).each do |path|
      next unless path.match?(/ruby-debug-ide/)
      load path + '/ruby-debug-ide/multiprocess/starter.rb'
    end
  end
end


text:
# frozen_string_literal: true

set :nvm_type, :user # or :system, depends on your nvm setup
set :nvm_node, 'v8.1.0'
set :nvm_map_bins, %w[node npm yarn]

set :rvm_ruby_version, '2.7.4@metasmoke'

set :puma_bind, %w[tcp://127.0.0.1:9292]
set :passenger_restart_with_touch, true

# set :bundle_path, nil

# server-based syntax
# ======================
# Defines a single server with a list of roles and multiple properties.
# You can define all roles on a single server, or split them:

server 'metasmoke.erwaysoftware.com', roles: %w[app db web]
# server 'example.com', user: 'deploy', roles: %w{app web}, other_property: :other_value
# server 'db.example.com', user: 'deploy', roles: %w{db}

# role-based syntax
# ==================

# Defines a role with one or multiple servers. The primary server in each
# group is considered to be the first unless any  hosts have the primary
# property set. Specify the username and a domain or IP for the server.
# Don't use `:all`, it's a meta role.

# role :app, %w{deploy@example.com}, my_property: :my_value
# role :web, %w{user1@primary.com user2@additional.com}, other_property: :other_value
# role :db,  %w{deploy@example.com}

# Configuration
# =============
# You can set any configuration variable like in config/deploy.rb
# These variables are then only loaded and set in this stage.
# For available Capistrano configuration variables see the documentation page.
# http://capistranorb.com/documentation/getting-started/configuration/
# Feel free to add new variables to customise your setup.

set :rails_env, 'production'

# Custom SSH Options
# ==================
# You may pass any option but keep in mind that net/ssh understands a
# limited set of options, consult the Net::SSH documentation.
# http://net-ssh.github.io/net-ssh/classes/Net/SSH.html#method-c-start
#
# Global options
# --------------
#  set :ssh_options, {
#    keys: %w(/home/rlisowski/.ssh/id_rsa),
#    forward_agent: false,
#    auth_methods: %w(password)
#  }
#
# The server-based syntax can be used to override options:
# ------------------------------------
server 'metasmoke.erwaysoftware.com',
       user: 'undo',
       roles: %w[web app db],
       ssh_options: {
         user: 'undo', # overrides user setting above
         port: 9923,
         keys: %w[sshkey.pem],
         forward_agent: false,
         auth_methods: %w[publickey]
       }


text:
// Common configuration for webpacker loaded from config/webpacker.yml

const { join, resolve } = require('path');
const { readFileSync } = require('fs');
const { env } = require('process');
const { safeLoad } = require('js-yaml');

const configPath = resolve('config', 'webpacker.yml');
const loadersDir = join(__dirname, 'loaders');
const settings = safeLoad(readFileSync(configPath), 'utf8')[env.NODE_ENV];

function removeOuterSlashes(string) {
  return string.replace(/^\/*/, '').replace(/\/*$/, '');
}

function formatPublicPath(host = '', path = '') {
  let formattedHost = removeOuterSlashes(host);
  if (formattedHost && !/^http/i.test(formattedHost)) {
    formattedHost = `//${formattedHost}`;
  }
  const formattedPath = removeOuterSlashes(path);
  return `${formattedHost}/${formattedPath}/`;
}

const output = {
  path: resolve('public', settings.public_output_path),
  publicPath: formatPublicPath(env.ASSET_HOST, settings.public_output_path)
};

module.exports = {
  settings,
  env,
  loadersDir,
  output
};


text:
// Note: You must restart bin/webpack-dev-server for changes to take effect

/* eslint global-require: 0 */
/* eslint import/no-dynamic-require: 0 */

const { basename, dirname, join, relative, resolve } = require('path');
const { sync } = require('glob');
const ExtractTextPlugin = require('extract-text-webpack-plugin');
const ManifestPlugin = require('webpack-manifest-plugin');
const webpack = require('webpack');
const extname = require('path-complete-extname');
const { env, settings, output, loadersDir } = require('./configuration.js');

const extensionGlob = `**/*{${settings.extensions.join(',')}}*`;
const entryPath = join(settings.source_path, settings.source_entry_path);
const packPaths = sync(join(entryPath, extensionGlob));

module.exports = {
  entry: packPaths.reduce(
    (map, entry) => {
      const localMap = map;
      const namespace = relative(join(entryPath), dirname(entry));
      const key = join(namespace, basename(entry, extname(entry)));
      localMap[key] = [resolve(entry)];
      if (entry.includes('vendor')) {
        localMap[key].unshift('babel-polyfill');
      }
      return localMap;
    }, {}
  ),

  output: {
    filename: '[name].js',
    path: output.path,
    publicPath: output.publicPath
  },

  module: {
    rules: sync(join(loadersDir, '*.js')).map(loader => require(loader))
  },

  plugins: [
    new webpack.EnvironmentPlugin(JSON.parse(JSON.stringify(env))),
    new ExtractTextPlugin(env.NODE_ENV === 'production' ? '[name]-[hash].css' : '[name].css'),
    new ManifestPlugin({
      publicPath: output.publicPath,
      writeToFileEmit: true
    }),
    new webpack.ProvidePlugin({
      jQuery: 'jquery',
      $: 'jquery'
    })
  ],

  externals: {
    jquery: 'jQuery',
    moment: 'moment'
  },

  resolve: {
    extensions: settings.extensions,
    modules: [
      resolve(settings.source_path),
      'node_modules'
    ]
  },

  resolveLoader: {
    modules: ['node_modules']
  }
};


text:
// Note: You must restart bin/webpack-dev-server for changes to take effect

const merge = require('webpack-merge');
const sharedConfig = require('./shared.js');

module.exports = merge(sharedConfig, {});


text:
// Note: You must restart bin/webpack-dev-server for changes to take effect

const merge = require('webpack-merge');
const CompressionPlugin = require('compression-webpack-plugin');
const BabiliPlugin = require('babili-webpack-plugin');

const sharedConfig = require('./shared');

module.exports = merge(sharedConfig, {
  output: { filename: '[name]-[chunkhash].js' },
  devtool: 'source-map',
  stats: 'normal',

  plugins: [
    new BabiliPlugin(),
    new CompressionPlugin({
      asset: '[path].gz[query]',
      algorithm: 'gzip',
      test: /\.(js|css|html|json|ico|svg|eot|otf|ttf)$/
    })
  ]
});


text:
// Note: You must restart bin/webpack-dev-server for changes to take effect

const merge = require('webpack-merge');
const sharedConfig = require('./shared.js');
const { settings, output } = require('./configuration.js');

module.exports = merge(sharedConfig, {
  devtool: 'cheap-module-source-map',

  stats: {
    errorDetails: true
  },

  output: {
    pathinfo: true
  },

  devServer: {
    clientLogLevel: 'none',
    https: settings.dev_server.https,
    host: settings.dev_server.host,
    port: settings.dev_server.port,
    contentBase: output.path,
    publicPath: output.publicPath,
    compress: true,
    headers: { 'Access-Control-Allow-Origin': '*' },
    historyApiFallback: true,
    disableHostCheck: true,
    watchOptions: {
      ignored: /node_modules/
    }
  }
});


text:
module.exports = {
  test: /\.erb$/,
  enforce: 'pre',
  exclude: /node_modules/,
  loader: 'rails-erb-loader',
  options: {
    runner: 'bin/rails runner'
  }
};


text:
module.exports = {
  test: /\.coffee(\.erb)?$/,
  loader: 'coffee-loader'
};


text:
const { env, publicPath } = require('../configuration.js');

module.exports = {
  test: /\.(jpg|jpeg|png|gif|svg|eot|ttf|woff|woff2)$/i,
  use: [{
    loader: 'file-loader',
    options: {
      publicPath,
      name: env.NODE_ENV === 'production' ? '[name]-[hash].[ext]' : '[name].[ext]'
    }
  }]
};


text:
module.exports = {
  test: /\.js(\.erb)?$/,
  exclude: /node_modules/,
  loader: 'babel-loader'
};


text:
const ExtractTextPlugin = require('extract-text-webpack-plugin');
const { env } = require('../configuration.js');

module.exports = {
  test: /\.(scss|sass|css)$/i,
  use: ExtractTextPlugin.extract({
    fallback: 'style-loader',
    use: [
      { loader: 'css-loader', options: { minimize: env.NODE_ENV === 'production' } },
      { loader: 'postcss-loader', options: { sourceMap: true } },
      'resolve-url-loader',
      { loader: 'sass-loader', options: { sourceMap: true } }
    ]
  })
};


text:
# frozen_string_literal: true

Rails.application.configure do
  # Settings specified here will take precedence over those in config/application.rb.

  # Code is not reloaded between requests.
  config.cache_classes = true

  # Eager load code on boot. This eager loads most of Rails and
  # your application in memory, allowing both threaded web servers
  # and those relying on copy on write to perform better.
  # Rake tasks automatically ignore this option for performance.
  config.eager_load = true

  # Full error reports are disabled and caching is turned on.
  config.consider_all_requests_local       = true
  config.action_controller.perform_caching = true

  # Enable Rack::Cache to put a simple HTTP cache in front of your application
  # Add `rack-cache` to your Gemfile before enabling this.
  # For large-scale production use, consider using a caching reverse proxy like
  # NGINX, varnish or squid.
  # config.action_dispatch.rack_cache = true

  # Disable serving static files from the `/public` folder by default since
  # Apache or NGINX already handles this.
  config.public_file_server.enabled = true

  config.public_file_server.headers = {
    'Cache-Control' => 'public, max-age=3600',
    'Expires' => 1.week.from_now.to_formatted_s(:rfc822).to_s
  }

  # Compress JavaScripts and CSS.
  # config.assets.js_compressor = :uglifier
  # config.assets.css_compressor = :sass

  # Do not fallback to assets pipeline if a precompiled asset is missed.
  config.assets.compile = false

  # Asset digests allow you to set far-future HTTP expiration dates on all assets,
  # yet still be able to expire them through the digest params.
  config.assets.digest = true

  # `config.assets.precompile` and `config.assets.version` have moved to config/initializers/assets.rb

  # Specifies the header that your server uses for sending files.
  # config.action_dispatch.x_sendfile_header = 'X-Sendfile' # for Apache
  # config.action_dispatch.x_sendfile_header = 'X-Accel-Redirect' # for NGINX

  # Force all access to the app over SSL, use Strict-Transport-Security, and use secure cookies.
  # config.force_ssl = true

  # Use the lowest log level to ensure availability of diagnostic information
  # when problems arise.
  config.log_level = :debug

  # Prepend all log lines with the following tags.
  # config.log_tags = [ :subdomain, :uuid ]

  # Use a different logger for distributed setups.
  # config.logger = ActiveSupport::TaggedLogging.new(SyslogLogger.new)

  # Use a different cache store in production.
  # config.cache_store = :memory_store, { size: 10.megabytes }

  # Enable serving of images, stylesheets, and JavaScripts from an asset server.
  # config.action_controller.asset_host = 'http://assets.example.com'

  # Ignore bad email addresses and do not raise email delivery errors.
  # Set this to true and configure the email server for immediate delivery to raise delivery errors.
  # config.action_mailer.raise_delivery_errors = false

  config.action_mailer.delivery_method = :smtp
  config.action_mailer.smtp_settings = {
    address: 'email-smtp.us-east-1.amazonaws.com',
    port: 587, # Port 25 is throttled on AWS
    user_name: AppConfig['ses_smtp_credentials']['username'],
    password: AppConfig['ses_smtp_credentials']['password'],
    authentication: :login
  }
  config.action_mailer.default_url_options = { host: 'metasmoke.erwaysoftware.com', port: 80 }

  # Enable locale fallbacks for I18n (makes lookups for any locale fall back to
  # the I18n.default_locale when a translation cannot be found).
  config.i18n.fallbacks = [I18n.default_locale]

  # Send deprecation notices to registered listeners.
  config.active_support.deprecation = :notify

  # Use default logging formatter so that PID and timestamp are not suppressed.
  config.log_formatter = ::Logger::Formatter.new

  # Do not dump schema after migrations.
  config.active_record.dump_schema_after_migration = false

  config.action_cable.disable_request_forgery_protection = true

  config.paperclip_defaults = {
    storage: :s3,
    s3_credentials: {
      bucket: 'erwaysoftware.metasmokedumps',
      access_key_id: AppConfig['aws']['access_token'],
      secret_access_key: AppConfig['aws']['secret_token']
    },
    s3_region: 'us-east-2'
  }

  Aws.config.update(region: 'us-east-1',
                    credentials: Aws::Credentials.new(AppConfig['aws']['access_token'], AppConfig['aws']['secret_token']))
end


text:
# frozen_string_literal: true

Rails.application.configure do
  # Settings specified here will take precedence over those in config/application.rb.

  # The test environment is used exclusively to run your application's
  # test suite. You never need to work with it otherwise. Remember that
  # your test database is 'scratch space' for the test suite and is wiped
  # and recreated between test runs. Don't rely on the data there!
  config.cache_classes = true

  # Do not eager load code on boot. This avoids loading your whole application
  # just for the purpose of running a single test. If you are using a tool that
  # preloads Rails for running tests, you may have to set it to true.
  config.eager_load = false

  # Configure static file server for tests with Cache-Control for performance.
  config.public_file_server.enabled = true
  config.public_file_server.headers = { 'Cache-Control' => 'public, max-age=3600' }

  # Show full error reports and disable caching.
  config.consider_all_requests_local       = true
  config.action_controller.perform_caching = false

  # Raise exceptions instead of rendering exception templates.
  config.action_dispatch.show_exceptions = false

  # Disable request forgery protection in test environment.
  config.action_controller.allow_forgery_protection = false

  # Tell Action Mailer not to deliver emails to the real world.
  # The :test delivery method accumulates sent emails in the
  # ActionMailer::Base.deliveries array.
  config.action_mailer.delivery_method = :test

  # Randomize the order test cases are executed.
  config.active_support.test_order = :random

  # Print deprecation notices to the stderr.
  config.active_support.deprecation = :stderr

  # Raises error for missing translations
  # config.action_view.raise_on_missing_translations = true
end


text:
# frozen_string_literal: true

Rails.application.configure do
  # Settings specified here will take precedence over those in config/application.rb.

  # In the development environment your application's code is reloaded on
  # every request. This slows down response time but is perfect for development
  # since you don't have to restart the web server when you make code changes.
  config.cache_classes = false

  # Do not eager load code on boot.
  config.eager_load = false

  # Show full error reports and disable caching.
  config.consider_all_requests_local       = true
  config.action_controller.perform_caching = false

  # Don't care if the mailer can't send.
  config.action_mailer.raise_delivery_errors = true

  config.action_mailer.delivery_method = :smtp
  config.action_mailer.smtp_settings = {
    address: 'email-smtp.us-east-1.amazonaws.com',
    port: 587, # Port 25 is throttled on AWS
    user_name: AppConfig['ses_smtp_credentials']['username'],
    password: AppConfig['ses_smtp_credentials']['password'],
    authentication: :login
  }

  config.action_mailer.default_url_options = { host: 'localhost' }

  # Print deprecation notices to the Rails logger.
  config.active_support.deprecation = :log

  # Raise an error on page load if there are pending migrations.
  config.active_record.migration_error = :page_load

  # Debug mode disables concatenation and preprocessing of assets.
  # This option may cause significant delays in view rendering with a large
  # number of complex assets.
  config.assets.debug = true

  # Asset digests allow you to set far-future HTTP expiration dates on all assets,
  # yet still be able to expire them through the digest params.
  config.assets.digest = true

  # Adds additional error checking when serving assets at runtime.
  # Checks for improperly declared sprockets dependencies.
  # Raises helpful error messages.
  config.assets.raise_runtime_errors = true

  # Raises error for missing translations
  # config.action_view.raise_on_missing_translations = true
  config.action_cable.disable_request_forgery_protection = true

  config.paperclip_defaults = {
    storage: :s3,
    s3_credentials: {
      bucket: 'erwaysoftware.metasmokedumps',
      access_key_id: AppConfig['aws']['access_token'],
      secret_access_key: AppConfig['aws']['secret_token']
    },
    s3_region: 'us-east-2'
  }

  Aws.config.update(region: 'us-east-1',
                    credentials: Aws::Credentials.new(AppConfig['aws']['access_token'], AppConfig['aws']['secret_token']))
end


text:
# frozen_string_literal: true
# Be sure to restart your server when you modify this file.

# ApplicationController.renderer.defaults.merge!(
#   http_host: 'example.org',
#   https: false
# )


text:
# Redis Schema:

HASH posts/<id> (Post)
        body: body,
        title: title,
        reason_weight: reasons.map(&:weight).reduce(:+),
        created_at: created_at,
        username: username,
        link: link,
        site_site_logo: site.try(:site_logo),
        stack_exchange_user_username: stack_exchange_user.try(:username),
        stack_exchange_user_id: stack_exchange_user.try(:id),
        flagged: flagged?,
        site_id: site_id,
        post_comments_count: comments.count,
        why: why

SET (currently ZSET) posts/<id>/feedbacks (Post.find(<id>).feedbacks.map(&:id))
ZSET posts/<id>/reasons (Post.reasons reason_name, score: weight)
-- These need AR callbacks
SET reasons (Reason.all.map(&:id))
SET reasons/<id> (Reason.all.map { |r| r.posts.map(&:id) }.flatten)
SET reasons/<id>/<feedback_type> Temporary cache used in /reasons
--
SET all_posts (Post.all.map(&:id))
ZSET posts (Post.all id, score: created_at.to_i)
SET tps (Post.where(is_tp:true).map(&:id))
SET fps (Post.where(is_fp:true).map(&:id))
SET naas (Post.where(is_naa:true).map(&:id))
SET nolink (Post.where(link:nil).map(&:id))
SET questions (Post.all.to_a.select(&:question?).map(&:id))
SET answers (Post.all.to_a.select(&:answer?).map(&:id))
SET autoflagged (Post.all.to_a.select(&:flagged?).map(&:id))
SET deleted (Post.where.not(deleted_at:nil).map(&:id))
SET sites/<id>/posts (Site.find(<id>).posts.map(&:id))
SET review_queues (ReivewQueue.map(&:id))
SET review_queue/<id>/unreviewed (ReviewItem.active.map(&:id))
feedbacks.each(&:populate_redis)
deletion_logs.each(&:update_deletion_data)


text:
# frozen_string_literal: true

require 'redis'
require 'active_record'

# In theory, I should PR the authors of redis-rb with this, but I'm lazy.
class Redis::Client
  protected

  def logging(commands)
    return yield unless @logger && @logger.debug?

    begin
      commands.each do |name, *args|
        logged_args = args.map do |a|
          if a.respond_to?(:inspect) then a.inspect
          elsif a.respond_to?(:to_s) then a.to_s
          else
            # handle poorly-behaved descendants of BasicObject
            klass = a.instance_exec { (class << self; self end).superclass }
            "\#<#{klass}:#{a.__id__}>"
          end
        end
        @logger.debug("[Redis] command=#{name.to_s.upcase} args=#{logged_args.join(' ')}")
      end

      t1 = Time.now
      yield
    ensure
      if t1
        call_time = (Time.now - t1) * 1000
        ActiveRecord::RuntimeRegistry.sql_runtime = ActiveRecord::RuntimeRegistry.sql_runtime.to_i + call_time
        @logger.debug('[Redis] call_time=%0.2f ms' % call_time) # rubocop:disable Style/FormatString
      end
    end
  end
end

class Redis
  # Yes, I know that monkey patching is bad, but redis is being bad too
  def hgetall(key)
    synchronize do |client|
      client.call([:hgetall, key], &proc { |r| Hashify.call(r.to_a) })
    end
  end
end

def redis(logger: false, new: false)
  if logger
    config = AppConfig['redis_logging']
    return Redis.new(config) if new
    $redis_logging ||= Redis.new(config) # rubocop:disable Style/GlobalVars
  else
    config = YAML.load_file(File.join(Rails.root, 'config', 'cable.yml'))[Rails.env]
    return Redis.new(config) if new
    $redis ||= Redis.new(config) # rubocop:disable Style/GlobalVars
  end
end

redis
redis(logger: true)

def with_no_score(ary)
  zeros = Array.new(ary.length) { 0 }
  elements = ary.map { |i| "\"#{i}\"" }
  zeros.zip(elements) # .flatten
end


text:
# frozen_string_literal: true

SensibleRoute.hook_rails


text:
# frozen_string_literal: true

# Be sure to restart your server when you modify this file.

# Enable per-form CSRF tokens.
Rails.application.config.action_controller.per_form_csrf_tokens = true


text:
# frozen_string_literal: true

# Be sure to restart your server when you modify this file.

# Version of your assets, change this if you want to expire all your assets.
Rails.application.config.assets.version = '1.0'

# Add additional assets to the asset load path
# Rails.application.config.assets.paths << Emoji.images_path

# Precompile additional assets.
# application.js, application.css, and all non-JS/CSS in app/assets folder are already added.
# Rails.application.config.assets.precompile += %w( search.js )

Rails.application.config.assets.precompile += %w[chartkick.js]
Rails.application.config.assets.precompile += %w[graphiql/rails/application.css]


text:
# frozen_string_literal: true

# Be sure to restart your server when you modify this file.

# Enable origin-checking CSRF mitigation.
Rails.application.config.action_controller.forgery_protection_origin_check = true


text:
# frozen_string_literal: true

Octokit.configure do |c|
  if AppConfig['github']['access_token'].present?
    c.access_token = AppConfig['github']['access_token']
  else
    c.login = AppConfig['github']['username']
    c.password = AppConfig['github']['password']
  end
end


text:
# frozen_string_literal: true

AppConfig = YAML.load_file("#{Rails.root}/config/config.yml")[Rails.env]


text:
# frozen_string_literal: true

Warden::Manager.after_set_user do |user, auth, opts|
  scope = opts[:scope]
  auth.cookies.signed["#{scope}.id"] = user.id
  auth.cookies.signed["#{scope}.expires_at"] = 30.minutes.from_now
end

Warden::Manager.before_logout do |_user, auth, opts|
  scope = opts[:scope]
  auth.cookies.signed["#{scope}.id"] = nil
  auth.cookies.signed["#{scope}.expires_at"] = nil
end


text:
# frozen_string_literal: true

if AppConfig['travis'].present? && AppConfig['travis']['token'].present?
  Travis.access_token = AppConfig['travis']['token']
end


text:
# frozen_string_literal: true

module SafeStoreLocation
  MAX_LOCATION_SIZE = ActionDispatch::Cookies::MAX_COOKIE_SIZE / 2

  def store_location_for(resource_or_scope, location)
    super unless location && location.size > MAX_LOCATION_SIZE
  end
end

Devise::FailureApp.include SafeStoreLocation


text:
# frozen_string_literal: true

require 'sensible_routes'

REDIS_LOG_EXPIRATION = 1.week.seconds.to_i

def sensible_routes_wrap(method, path)
  r = nil
  Rails.application.routes.instance_variable_get(:@router)
       .recognize(ActionDispatch::Request.new(Rack::MockRequest.env_for(path, method: method))) do |rt|
         r = SensibleRoute.new(rt)
       end
  r
end

# rubocop:disable Metrics/ParameterLists
def log_timestamps(ts, status:, action:, controller:, format:, method:, # rubocop:disable Lint/UnusedMethodArgument
                   view_runtime:, db_runtime:, path:, uuid:) # rubocop:disable Lint/UnusedMethodArgument
  # rubocop:enable Metrics/ParameterLists
  redis_logger = redis(logger: true)
  view_runtime = view_runtime.to_f
  db_runtime = db_runtime.to_f

  return if path.nil?
  path = sensible_routes_wrap(method, path)&.path || path.split('?').first
  path_string = "#{method.upcase}/#{path}.#{format}"
  # controller_action_string = "#{controller}##{action}"
  redis_logger.sadd 'request_timings/path_strings', path_string
  # redis_logger.sadd "request_timings/controller_action_strings", controller_action_string
  redis_logger.zadd "request_timings/view/by_path/#{path_string}", ts, view_runtime
  redis_logger.zadd "request_timings/db/by_path/#{path_string}", ts, db_runtime
  redis_logger.zadd "request_timings/total/by_path/#{path_string}", ts, (db_runtime + view_runtime)
  redis_logger.zremrangebyscore "request_timings/view/by_path/#{path_string}", '-inf', ts - REDIS_LOG_EXPIRATION
  redis_logger.zremrangebyscore "request_timings/db/by_path/#{path_string}", '-inf', ts - REDIS_LOG_EXPIRATION
  redis_logger.zremrangebyscore "request_timings/total/by_path/#{path_string}", '-inf', ts - REDIS_LOG_EXPIRATION
  redis_logger.expire "request_timings/view/by_path/#{path_string}", REDIS_LOG_EXPIRATION
  redis_logger.expire "request_timings/db/by_path/#{path_string}", REDIS_LOG_EXPIRATION
  redis_logger.expire "request_timings/total/by_path/#{path_string}", REDIS_LOG_EXPIRATION

  # redis_logger.zadd "request_timings/view/by_action/#{controller_action_string}", ts, view_runtime
  # redis_logger.zadd "request_timings/db/by_action/#{controller_action_string}", ts, db_runtime
  # redis_logger.zadd "request_timings/total/by_action/#{controller_action_string}", ts, (db_runtime + view_runtime)

  # redis_logger.zadd "request_timings/status_counts/by_path/#{path_string}", ts, status
  # redis_logger.zadd "request_timings/status_counts/by_action/#{controller_action_string}", ts, status
  # redis_logger.zadd 'request_timings/status_counts', ts, status

  # redis_logger.zadd "requests/by_path/#{path_string}", ts, uuid
  # redis_logger.zadd "requests/by_action/#{controller_action_string}", ts, uuid

  # redis_logger.zadd 'request_timings/sha', ts, CurrentCommit, nx: true
end

ActiveSupport::Notifications.subscribe 'process_action.action_controller' do |_name, _started, _finished, _unique_id, data|
  redis_logger = redis(logger: true)
  request_id = data[:headers]['action_dispatch.request_id']
  # redis_log_id = data[:headers]['rack.session']['redis_log_id']
  redis_log_key = data[:headers]['redis_logs.log_key']
  request_timestamp = data[:headers]['redis_logs.timestamp']
  unless request_timestamp.nil?
    # RedisLogJob.perform_later(
    #   data.slice(:controller, :action, :format, :method, :status, :view_runtime, :db_runtime),
    #   subspaces: {
    #     response_headers: data[:headers].to_h['action_controller.instance'].response.headers.to_h
    #   },
    #   status: data[:status],
    #   exception: data.slice(:exception, :exception_object),
    #   time: request_timestamp,
    #   uuid: request_id,
    #   completed: true
    # )
    unless data[:status].nil?
      log_timestamps(request_timestamp, **data.slice(
        :action, :controller,
        :view_runtime, :db_runtime,
        :method, :format, :status
      ), path: data[:path] || redis_logger.hget(redis_log_key, 'path'), uuid: request_id)
    end
  end
end

ActiveSupport::Notifications.subscribe 'endpoint_run.grape' do |_name, _started, _finished, _unique_id, data|
  # request_id = data[:env]['action_dispatch.request_id']
  # # redis_log_id = data[:env]['rack.session']['redis_log_id']
  # request_timestamp = data[:env]['redis_logs.timestamp']
  #
  # RedisLogJob.perform_later(
  #   {
  #     # The API doesn't spit out that much, so I'm doing what I can
  #     controller: nil,
  #     action: nil,
  #     format: data[:env]['api.endpoint'].headers['Content-Type'],
  #     method: data[:env]['grape.routing_args'][:route_info].request_method,
  #     status: 'API',
  #     view_runtime: nil,
  #     db_runtime: Time.now.to_f - data[:env]['redis_logs.start_time'].to_f
  #   },
  #   exception: data.slice(:exception, :exception_object),
  #   time: request_timestamp,
  #   uuid: request_id,
  #   completed: true
  # )
end


text:
# frozen_string_literal: true

# Be sure to restart your server when you modify this file.

# Specify a serializer for the signed and encrypted cookie jars.
# Valid options are :json, :marshal, and :hybrid.
Rails.application.config.action_dispatch.cookies_serializer = :json


text:
# frozen_string_literal: true
# Be sure to restart your server when you modify this file.

# Add new mime types for use in respond_to blocks:
# Mime::Type.register 'text/richtext', :rtf


text:
# frozen_string_literal: true

Rolify.configure do |config|
  # By default ORM adapter is ActiveRecord. uncomment to use mongoid
  # config.use_mongoid

  # Dynamic shortcuts for User class (user.is_admin? like methods). Default is: false
  # config.use_dynamic_shortcuts
end


text:
# frozen_string_literal: true
# Be sure to restart your server when you modify this file.

# You can add backtrace silencers for libraries that you're using but don't wish to see in your backtraces.
# Rails.backtrace_cleaner.add_silencer { |line| line =~ /my_noisy_library/ }

# You can also remove all the silencers if you're trying to debug a problem that might stem from framework code.
# Rails.backtrace_cleaner.remove_silencers!


text:
# frozen_string_literal: true

# Be sure to restart your server when you modify this file.

# This file contains settings for ActionController::ParamsWrapper which
# is enabled by default.

# Enable parameter wrapping for JSON. You can disable this by setting :format to an empty array.
ActiveSupport.on_load(:action_controller) do
  wrap_parameters format: [:json]
end

# To enable root element in JSON for ActiveRecord objects.
# ActiveSupport.on_load(:active_record) do
#   self.include_root_in_json = true
# end


text:
# frozen_string_literal: true

CurrentCommit = File.readable?('REVISION') ? File.read('REVISION').strip : `git rev-parse --short HEAD`.chomp # rubocop:disable Style/ConstantName


text:
# frozen_string_literal: true

ActiveSupport::Inflector.inflections(:en) do |inflect|
  inflect.acronym 'RSS'
  inflect.acronym 'API'
end


text:
# frozen_string_literal: true

# This was an attempt to fix introspection queries taking up boatloads of complexity.
# it failed.

# module GraphQL
#   class Field
#     def complexity=(something)
#       "You lose"
#       nil
#     end
#
#     def complexity
#       @real_complexity || 0
#     end
#
#     def real_complexity=(complexity)
#       @real_complexity = complexity
#     end
#
#     def initialize
#       @real_complexity = 0
#       @complexity = 0
#       @arguments = {}
#       @resolve_proc = build_default_resolver
#       @lazy_resolve_proc = DefaultLazyResolve
#       @relay_node_field = false
#       @connection = false
#       @connection_max_page_size = nil
#       @edge_class = nil
#       @trace = nil
#       @introspection = false
#     end
#   end
#
#   class Function
#     def self.complexity
#       self.class.complexity || 0
#     end
#   end
# end

# module GraphQL
#   class Field
#     def initialize
#       @complexity = 1
#       @arguments = {}
#       @resolve_proc = build_default_resolver
#       @lazy_resolve_proc = DefaultLazyResolve
#       @relay_node_field = false
#       @connection = false
#       @connection_max_page_size = nil
#       @edge_class = nil
#       @trace = nil
#       @introspection = false
#     end
#   end
# end

# Logging of query complexity
log_query_complexity = GraphQL::Analysis::QueryComplexity.new { |_query, complexity| Rails.logger.info("[GraphQL Query Complexity] #{complexity}") }
MetasmokeSchema.query_analyzers << log_query_complexity