From 2eb5ffb9b326b4167a2df22102e0e9dba88385f5 Mon Sep 17 00:00:00 2001 From: KMY Date: Sun, 23 Apr 2023 14:20:07 +0900 Subject: [PATCH] Wip: antenna --- app/controllers/antennas_controller.rb | 76 +++++++ app/models/antenna.rb | 203 +++++++++++++++++++ app/models/antenna_account.rb | 17 ++ app/models/antenna_domain.rb | 16 ++ app/models/antenna_tag.rb | 17 ++ app/models/concerns/account_associations.rb | 2 + app/models/list.rb | 1 + app/models/tag.rb | 2 + app/services/fan_out_on_write_service.rb | 25 +++ app/views/antennas/_antenna.html.haml | 65 ++++++ app/views/antennas/_antenna_fields.html.haml | 45 ++++ app/views/antennas/_keyword_fields.html.haml | 8 + app/views/antennas/edit.html.haml | 8 + app/views/antennas/index.html.haml | 11 + app/views/antennas/new.html.haml | 8 + config/navigation.rb | 1 + config/routes.rb | 1 + db/migrate/20230423002728_create_antennas.rb | 40 ++++ db/schema.rb | 65 +++++- 19 files changed, 610 insertions(+), 1 deletion(-) create mode 100644 app/controllers/antennas_controller.rb create mode 100644 app/models/antenna.rb create mode 100644 app/models/antenna_account.rb create mode 100644 app/models/antenna_domain.rb create mode 100644 app/models/antenna_tag.rb create mode 100644 app/views/antennas/_antenna.html.haml create mode 100644 app/views/antennas/_antenna_fields.html.haml create mode 100644 app/views/antennas/_keyword_fields.html.haml create mode 100644 app/views/antennas/edit.html.haml create mode 100644 app/views/antennas/index.html.haml create mode 100644 app/views/antennas/new.html.haml create mode 100644 db/migrate/20230423002728_create_antennas.rb diff --git a/app/controllers/antennas_controller.rb b/app/controllers/antennas_controller.rb new file mode 100644 index 0000000000..b7639d0e27 --- /dev/null +++ b/app/controllers/antennas_controller.rb @@ -0,0 +1,76 @@ +# frozen_string_literal: true + +class AntennasController < ApplicationController + layout 'admin' + + before_action :authenticate_user! + before_action :set_antenna, only: [:edit, :update, :destroy] + before_action :set_lists, only: [:new, :edit] + before_action :set_body_classes + before_action :set_cache_headers + + def index + @antennas = current_account.antennas.includes(:antenna_domains).includes(:antenna_tags).includes(:antenna_accounts) + end + + def new + @antenna = current_account.antennas.build + @antenna.antenna_domains.build + @antenna.antenna_tags.build + @antenna.antenna_accounts.build + end + + def create + @antenna = current_account.antennas.build(thin_resource_params) + + saved = @antenna.save + saved = @antenna.update(resource_params) if saved + + if saved + redirect_to antennas_path + else + render action: :new + end + end + + def edit; end + + def update + if @antenna.update(resource_params) + redirect_to antennas_path + else + render action: :edit + end + end + + def destroy + @antenna.destroy + redirect_to antennas_path + end + + private + + def set_antenna + @antenna = current_account.antennas.find(params[:id]) + end + + def set_lists + @lists = current_account.owned_lists + end + + def resource_params + params.require(:antenna).permit(:title, :list, :available, :expires_in, :keywords_raw, :exclude_keywords_raw, :domains_raw, :exclude_domains_raw, :accounts_raw, :exclude_accounts_raw, :tags_raw, :exclude_tags_raw) + end + + def thin_resource_params + params.require(:antenna).permit(:title, :list) + end + + def set_body_classes + @body_classes = 'admin' + end + + def set_cache_headers + response.cache_control.replace(private: true, no_store: true) + end +end diff --git a/app/models/antenna.rb b/app/models/antenna.rb new file mode 100644 index 0000000000..9ee12f132b --- /dev/null +++ b/app/models/antenna.rb @@ -0,0 +1,203 @@ +# == Schema Information +# +# Table name: antennas +# +# id :bigint(8) not null, primary key +# account_id :bigint(8) not null +# list_id :bigint(8) not null +# title :string default(""), not null +# keywords :jsonb +# exclude_keywords :jsonb +# any_domains :boolean default(TRUE), not null +# any_tags :boolean default(TRUE), not null +# any_accounts :boolean default(TRUE), not null +# any_keywords :boolean default(TRUE), not null +# available :boolean default(TRUE), not null +# created_at :datetime not null +# updated_at :datetime not null +# expires_at :datetime +# +class Antenna < ApplicationRecord + include Expireable + + has_many :antenna_domains, inverse_of: :antenna, dependent: :destroy + has_many :antenna_tags, inverse_of: :antenna, dependent: :destroy + has_many :antenna_accounts, inverse_of: :antenna, dependent: :destroy + + belongs_to :account + belongs_to :list + + scope :all_keywords, -> { where(any_keywords: true) } + scope :all_domains, -> { where(any_domains: true) } + scope :all_accounts, -> { where(any_accounts: true) } + scope :all_tags, -> { where(any_tags: true) } + scope :availables, -> { where(available: true) } + + def enabled? + available && !(any_keywords && any_domains && any_accounts && any_tags) && !expires? + end + + def expires_in + return @expires_in if defined?(@expires_in) + return nil if expires_at.nil? + + [30.minutes, 1.hour, 6.hours, 12.hours, 1.day, 1.week].find { |expires_in| expires_in.from_now >= expires_at } + end + + def expires? + expires_at.present? && expires_at < Time.now.utc + end + + def context + context = [] + context << 'domain' if !any_domains + context << 'tag' if !any_tags + context << 'keyword' if !any_keywords + context << 'account' if !any_accounts + context + end + + def list=(list_id) + list_id = list_id.to_i if list_id.is_a?(String) + if list_id.is_a?(Numeric) + self[:list_id] = list_id + else + self[:list] = list_id + end + end + + def keywords_raw + return '' if !keywords.present? + + keywords.join("\n") + end + + def keywords_raw=(raw) + keywords = raw.split(/\R/).filter { |r| r.present? } + self[:keywords] = keywords + self[:any_keywords] = !keywords.any? && !exclude_keywords&.any? + end + + def exclude_keywords_raw + return '' if !exclude_keywords.present? + + exclude_keywords.join("\n") + end + + def exclude_keywords_raw=(raw) + exclude_keywords = raw.split(/\R/).filter { |r| r.present? } + self[:exclude_keywords] = exclude_keywords + self[:any_keywords] = !keywords&.any? && !exclude_keywords.any? + end + + def tags_raw + antenna_tags.where(exclude: false).map(&:tag).map(&:name).join("\n") + end + + def tags_raw=(raw) + return if tags_raw == raw + + tag_names = raw.split(/\R/).filter { |r| r.present? } + + antenna_tags.where(exclude: false).destroy_all! + Tag.find_or_create_by_names(tag_names).each do |tag| + antenna_tags.create!(tag: tag, exclude: false) + end + self[:any_tags] = !tag_names.any? && !exclude_tags&.any? + end + + def exclude_tags_raw + antenna_tags.where(exclude: true).map(&:tag).map(&:name).join("\n") + end + + def exclude_tags_raw=(raw) + return if exclude_tags_raw == raw + + tag_names = raw.split(/\R/).filter { |r| r.present? } + + antenna_tags.where(exclude: true).destroy_all! + Tag.find_or_create_by_names(tag_names).each do |tag| + antenna_tags.create!(tag: tag, exclude: true) + end + self[:any_tags] = !tag_names.any? && !tags&.any? + end + + def domains_raw + antenna_domains.where(exclude: false).map(&:name).join("\n") + end + + def domains_raw=(raw) + return if domains_raw == raw + + domain_names = raw.split(/\R/).filter { |r| r.present? } + + antenna_domains.where(exclude: false).destroy_all! + domain_names.each do |domain| + antenna_domains.create!(name: domain, exclude: false) + end + self[:any_domains] = !domain_names.any? && !exclude_domains&.any? + end + + def exclude_domains_raw + antenna_domains.where(exclude: true).map(&:name).join("\n") + end + + def exclude_domains_raw=(raw) + return if exclude_domains_raw == raw + + domain_names = raw.split(/\R/).filter { |r| r.present? } + + antenna_domains.where(exclude: true).destroy_all! + domain_names.each do |domain| + antenna_domains.create!(name: domain, exclude: true) + end + self[:any_domains] = !domain_names.any? && !domains&.any? + end + + def accounts_raw + antenna_accounts.where(exclude: false).map(&:account).map { |account| account.domain ? "@#{account.username}@#{account.domain}" : "@#{account.username}" }.join("\n") + end + + def accounts_raw=(raw) + return if accounts_raw == raw + + account_names = raw.split(/\R/).filter { |r| r.present? } + + hit = false + antenna_accounts.where(exclude: false).destroy_all! + account_names.each do |name| + name = name[1..-1] if name.start_with?('@') + username, domain = name.split('@') + account = Account.find_by(username: username, domain: domain) + if account.present? + antenna_accounts.create!(account: account, exclude: false) + hit = true + end + end + self[:any_accounts] = !hit && !exclude_accounts&.any? + end + + def exclude_accounts_raw + antenna_accounts.where(exclude: true).map(&:account).map { |account| account.domain ? "@#{account.username}@#{account.domain}" : "@#{account.username}" }.join("\n") + end + + def exclude_accounts_raw=(raw) + return if exclude_accounts_raw == raw + + account_names = raw.split(/\R/).filter { |r| r.present? } + + hit = false + antenna_accounts.where(exclude: true).destroy_all! + account_names.each do |name| + name = name[1..-1] if name.start_with?('@') + username, domain = name.split('@') + account = Account.find_by(username: username, domain: domain) + if account.present? + antenna_accounts.create!(account: account, exclude: true) + hit = true + end + end + self[:any_accounts] = !hit && !accounts&.any? + end + +end diff --git a/app/models/antenna_account.rb b/app/models/antenna_account.rb new file mode 100644 index 0000000000..d92e5535f9 --- /dev/null +++ b/app/models/antenna_account.rb @@ -0,0 +1,17 @@ +# == Schema Information +# +# Table name: antenna_accounts +# +# id :bigint(8) not null, primary key +# antenna_id :bigint(8) not null +# account_id :bigint(8) not null +# exclude :boolean default(FALSE), not null +# created_at :datetime not null +# updated_at :datetime not null +# +class AntennaAccount < ApplicationRecord + + belongs_to :antenna + belongs_to :account + +end diff --git a/app/models/antenna_domain.rb b/app/models/antenna_domain.rb new file mode 100644 index 0000000000..aff087292d --- /dev/null +++ b/app/models/antenna_domain.rb @@ -0,0 +1,16 @@ +# == Schema Information +# +# Table name: antenna_domains +# +# id :bigint(8) not null, primary key +# antenna_id :bigint(8) not null +# name :string +# exclude :boolean default(FALSE), not null +# created_at :datetime not null +# updated_at :datetime not null +# +class AntennaDomain < ApplicationRecord + + belongs_to :antenna + +end diff --git a/app/models/antenna_tag.rb b/app/models/antenna_tag.rb new file mode 100644 index 0000000000..fff5208b5b --- /dev/null +++ b/app/models/antenna_tag.rb @@ -0,0 +1,17 @@ +# == Schema Information +# +# Table name: antenna_tags +# +# id :bigint(8) not null, primary key +# antenna_id :bigint(8) not null +# tag_id :bigint(8) not null +# exclude :boolean default(FALSE), not null +# created_at :datetime not null +# updated_at :datetime not null +# +class AntennaTag < ApplicationRecord + + belongs_to :antenna + belongs_to :tag + +end diff --git a/app/models/concerns/account_associations.rb b/app/models/concerns/account_associations.rb index b76892a9d6..00c57f00d9 100644 --- a/app/models/concerns/account_associations.rb +++ b/app/models/concerns/account_associations.rb @@ -39,6 +39,8 @@ module AccountAssociations has_many :report_notes, dependent: :destroy has_many :custom_filters, inverse_of: :account, dependent: :destroy + has_many :antennas, inverse_of: :account, dependent: :destroy + has_many :antenna_accounts, inverse_of: :account, dependent: :destroy # Moderation notes has_many :account_moderation_notes, dependent: :destroy, inverse_of: :account diff --git a/app/models/list.rb b/app/models/list.rb index bd1bdbd24d..c5e3a62d70 100644 --- a/app/models/list.rb +++ b/app/models/list.rb @@ -23,6 +23,7 @@ class List < ApplicationRecord has_many :list_accounts, inverse_of: :list, dependent: :destroy has_many :accounts, through: :list_accounts + has_many :antennas, inverse_of: :list, dependent: :destroy validates :title, presence: true diff --git a/app/models/tag.rb b/app/models/tag.rb index 9fb32cd06e..d023b335a2 100644 --- a/app/models/tag.rb +++ b/app/models/tag.rb @@ -27,6 +27,8 @@ class Tag < ApplicationRecord has_many :featured_tags, dependent: :destroy, inverse_of: :tag has_many :followers, through: :passive_relationships, source: :account + has_one :antenna_tag, dependent: :destroy, inverse_of: :tag + HASHTAG_SEPARATORS = "_\u00B7\u30FB\u200c" HASHTAG_FIRST_SEQUENCE_CHUNK_ONE = "[[:word:]_][[:word:]#{HASHTAG_SEPARATORS}]*[[:alpha:]#{HASHTAG_SEPARATORS}]" HASHTAG_FIRST_SEQUENCE_CHUNK_TWO = "[[:word:]#{HASHTAG_SEPARATORS}]*[[:word:]_]" diff --git a/app/services/fan_out_on_write_service.rb b/app/services/fan_out_on_write_service.rb index 9f7a8441cc..c18224e4be 100644 --- a/app/services/fan_out_on_write_service.rb +++ b/app/services/fan_out_on_write_service.rb @@ -49,6 +49,7 @@ class FanOutOnWriteService < BaseService when :public, :unlisted, :public_unlisted, :private deliver_to_all_followers! deliver_to_lists! + deliver_to_antennas! if [:public, :public_unlisted].include?(@status.visibility.to_sym) when :limited deliver_to_mentioned_followers! else @@ -115,6 +116,30 @@ class FanOutOnWriteService < BaseService end end + def deliver_to_antennas! + lists = [] + antennas = Antenna.availables + p '=========================== DEBUG A ' + antennas.size.to_s + antennas = antennas.merge!(Antenna.where(any_accounts: true).or(Antenna.joins(:antenna_accounts).where(antenna_accounts: { account: @status.account }).map(&:antenna))) + p '=========================== DEBUG B ' + antennas.size.to_s + p '=========================== DEBUG C ' + antennas.size.to_s + p '=========================== DEBUG D ' + antennas.size.to_s + antennas.in_batches do |ans| + ans.each do |antenna| + next if !antenna.enabled? + next if antenna.keywords.any? && !@status.text.include?(antenna.keywords) + next if antenna.exclude_keywords.any? && @status.text.include?(antenna.exclude_keywords) + lists << antenna.list + end + end + + if lists.any? + FeedInsertWorker.push_bulk(lists) do |list| + [@status.id, list.id, 'list', { 'update' => update? }] + end + end + end + def deliver_to_mentioned_followers! @status.mentions.joins(:account).merge(@account.followers_for_local_distribution).select(:id, :account_id).reorder(nil).find_in_batches do |mentions| FeedInsertWorker.push_bulk(mentions) do |mention| diff --git a/app/views/antennas/_antenna.html.haml b/app/views/antennas/_antenna.html.haml new file mode 100644 index 0000000000..4f365f7f52 --- /dev/null +++ b/app/views/antennas/_antenna.html.haml @@ -0,0 +1,65 @@ +.filters-list__item{ class: [antenna.expired? && 'expired'] } + = link_to edit_antenna_path(antenna), class: 'filters-list__item__title' do + = antenna.title + + - if antenna.expires? + .expiration{ title: t('antennas.index.expires_on', date: l(antenna.expires_at)) } + - if antenna.expired? + = t('invites.expired') + - else + = t('antennas.index.expires_in', distance: distance_of_time_in_words_to_now(antenna.expires_at)) + + .filters-list__item__permissions + %ul.permissions-list + - unless antenna.antenna_domains.empty? + %li.permissions-list__item + .permissions-list__item__icon + = fa_icon('sitemap') + .permissions-list__item__text + .permissions-list__item__text__title + = t('antennas.index.domains', count: antenna.antenna_domains.size) + .permissions-list__item__text__type + - domains = antenna.antenna_domains + - domains = domains.take(5) + ['…'] if domains.size > 5 # TODO + = domains.join(', ') + - unless antenna.antenna_accounts.empty? + %li.permissions-list__item + .permissions-list__item__icon + = fa_icon('users') + .permissions-list__item__text + .permissions-list__item__text__title + = t('antennas.index.accounts', count: antenna.antenna_accounts.size) + .permissions-list__item__text__type + - accounts = antenna.antenna_accounts.map { |account| account.account.domain ? "@#{account.account.username}@#{account.account.domain}" : "@#{account.account.username}" } + - accounts = accounts.take(5) + ['…'] if accounts.size > 5 # TODO + = accounts.join(', ') + - unless antenna.keywords.nil? || antenna.keywords.empty? + %li.permissions-list__item + .permissions-list__item__icon + = fa_icon('paragraph') + .permissions-list__item__text + .permissions-list__item__text__title + = t('antennas.index.keywords', count: antenna.keywords.size) + .permissions-list__item__text__type + - keywords = antenna.keywords + - keywords = keywords.take(5) + ['…'] if keywords.size > 5 # TODO + = keywords.join(', ') + - unless antenna.antenna_tags.empty? + %li.permissions-list__item + .permissions-list__item__icon + = fa_icon('hashtag') + .permissions-list__item__text + .permissions-list__item__text__title + = t('antennas.index.tags', count: antenna.antenna_tags.size) + .permissions-list__item__text__type + - tags = antenna.antenna_tags.map { |tag| tag.tag.name } + - tags = keywords.take(5) + ['…'] if tags.size > 5 # TODO + = tags.join(', ') + + .announcements-list__item__action-bar + .announcements-list__item__meta + = t('antenna.index.contexts', contexts: antenna.context.map { |context| I18n.t("antenna.contexts.#{context}") }.join(', ')) + + %div + = table_link_to 'pencil', t('antennas.edit.title'), edit_antenna_path(antenna) + = table_link_to 'times', t('antennas.index.delete'), antenna_path(antenna), method: :delete, data: { confirm: t('admin.accounts.are_you_sure') } diff --git a/app/views/antennas/_antenna_fields.html.haml b/app/views/antennas/_antenna_fields.html.haml new file mode 100644 index 0000000000..e41ab54188 --- /dev/null +++ b/app/views/antennas/_antenna_fields.html.haml @@ -0,0 +1,45 @@ +.fields-row + .fields-row__column.fields-row__column-6.fields-group + = f.input :title, as: :string, wrapper: :with_label, hint: false + .fields-row__column.fields-row__column-6.fields-group + = f.input :expires_in, wrapper: :with_label, collection: [30.minutes, 1.hour, 6.hours, 12.hours, 1.day, 1.week].map(&:to_i), label_method: lambda { |i| I18n.t("invites.expires_in.#{i}") }, include_blank: I18n.t('invites.expires_in_prompt') + +.fields-row + .fields-group.fields-row__column.fields-row__column-6 + = f.input :list, collection: lists, wrapper: :with_label, label_method: lambda { |list| list.title }, selected: f.object.list&.id, hint: false + .fields-group.fields-row__column.fields-row__column-6 + = f.input :available, wrapper: :with_label, hint: false + +%hr.spacer/ + +%h4= t('antennas.edit.domains') + +.fields-row + .fields-row__column.fields-row__column-6.fields-group + = f.input :domains_raw, wrapper: :with_label, as: :text, input_html: { rows: 5 } + .fields-row__column.fields-row__column-6.fields-group + = f.input :exclude_domains_raw, wrapper: :with_label, as: :text, input_html: { rows: 5 } + +%h4= t('antennas.edit.accounts') + +.fields-row + .fields-row__column.fields-row__column-6.fields-group + = f.input :accounts_raw, wrapper: :with_label, as: :text, input_html: { rows: 5 } + .fields-row__column.fields-row__column-6.fields-group + = f.input :exclude_accounts_raw, wrapper: :with_label, as: :text, input_html: { rows: 5 } + +%h4= t('antennas.edit.tags') + +.fields-row + .fields-row__column.fields-row__column-6.fields-group + = f.input :tags_raw, wrapper: :with_label, as: :text, input_html: { rows: 5 } + .fields-row__column.fields-row__column-6.fields-group + = f.input :exclude_tags_raw, wrapper: :with_label, as: :text, input_html: { rows: 5 } + +%h4= t('antennas.edit.keywords') + +.fields-row + .fields-row__column.fields-row__column-6.fields-group + = f.input :keywords_raw, wrapper: :with_label, as: :text, input_html: { rows: 5 } + .fields-row__column.fields-row__column-6.fields-group + = f.input :exclude_keywords_raw, wrapper: :with_label, as: :text, input_html: { rows: 5 } diff --git a/app/views/antennas/_keyword_fields.html.haml b/app/views/antennas/_keyword_fields.html.haml new file mode 100644 index 0000000000..a0ebdb1a04 --- /dev/null +++ b/app/views/antennas/_keyword_fields.html.haml @@ -0,0 +1,8 @@ +%tr.nested-fields + %td= f.input :keyword, as: :string + %td + .label_input__wrapper= f.input_field :whole_word + %td + = f.hidden_field :id if f.object&.persisted? # Required so Rails doesn't put the field outside of the + = link_to_remove_association(f, class: 'table-action-link') do + = safe_join([fa_icon('times'), t('antennas.index.delete')]) diff --git a/app/views/antennas/edit.html.haml b/app/views/antennas/edit.html.haml new file mode 100644 index 0000000000..7452df8e40 --- /dev/null +++ b/app/views/antennas/edit.html.haml @@ -0,0 +1,8 @@ +- content_for :page_title do + = t('antennas.edit.title') + += simple_form_for @antenna, url: antenna_path(@antenna), method: :put do |f| + = render 'antenna_fields', f: f, lists: @lists + + .actions + = f.button :button, t('generic.save_changes'), type: :submit diff --git a/app/views/antennas/index.html.haml b/app/views/antennas/index.html.haml new file mode 100644 index 0000000000..d0b9bbcc14 --- /dev/null +++ b/app/views/antennas/index.html.haml @@ -0,0 +1,11 @@ +- content_for :page_title do + = t('antennas.index.title') + +- content_for :heading_actions do + = link_to t('antennas.new.title'), new_antenna_path, class: 'button' + +- if @antennas.empty? + .muted-hint.center-text= t 'antennas.index.empty' +- else + .applications-list + = render partial: 'antenna', collection: @antennas diff --git a/app/views/antennas/new.html.haml b/app/views/antennas/new.html.haml new file mode 100644 index 0000000000..c53c7bc002 --- /dev/null +++ b/app/views/antennas/new.html.haml @@ -0,0 +1,8 @@ +- content_for :page_title do + = t('antennas.new.title') + += simple_form_for @antenna, url: antennas_path do |f| + = render 'antenna_fields', f: f, lists: @lists + + .actions + = f.button :button, t('antennas.new.save'), type: :submit diff --git a/config/navigation.rb b/config/navigation.rb index 758ea96d15..4433cdfc24 100644 --- a/config/navigation.rb +++ b/config/navigation.rb @@ -17,6 +17,7 @@ SimpleNavigation::Configuration.run do |navigation| n.item :relationships, safe_join([fa_icon('users fw'), t('settings.relationships')]), relationships_path, if: -> { current_user.functional? } n.item :filters, safe_join([fa_icon('filter fw'), t('filters.index.title')]), filters_path, highlights_on: %r{/filters}, if: -> { current_user.functional? } + n.item :antennas, safe_join([fa_icon('wifi fw'), t('antennas.index.title')]), antennas_path n.item :statuses_cleanup, safe_join([fa_icon('history fw'), t('settings.statuses_cleanup')]), statuses_cleanup_path, if: -> { current_user.functional_or_moved? } n.item :security, safe_join([fa_icon('lock fw'), t('settings.account')]), edit_user_registration_path do |s| diff --git a/config/routes.rb b/config/routes.rb index 109ad8f954..6f1b1ad555 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -216,6 +216,7 @@ Rails.application.routes.draw do end end end + resources :antennas, except: [:show] resource :relationships, only: [:show, :update] resource :statuses_cleanup, controller: :statuses_cleanup, only: [:show, :update] diff --git a/db/migrate/20230423002728_create_antennas.rb b/db/migrate/20230423002728_create_antennas.rb new file mode 100644 index 0000000000..b2a3c89ac4 --- /dev/null +++ b/db/migrate/20230423002728_create_antennas.rb @@ -0,0 +1,40 @@ +class CreateAntennas < ActiveRecord::Migration[6.1] + def change + create_table :antennas do |t| + t.belongs_to :account, null: false, foreign_key: { on_delete: :cascade } + t.belongs_to :list, null: false, foreign_key: { on_delete: :cascade } + t.string :title, null: false, default: '' + t.jsonb :keywords + t.jsonb :exclude_keywords + t.boolean :any_domains, null: false, default: true, index: true + t.boolean :any_tags, null: false, default: true, index: true + t.boolean :any_accounts, null: false, default: true, index: true + t.boolean :any_keywords, null: false, default: true, index: true + t.boolean :available, null: false, default: true, index: true + t.datetime :created_at, null: false + t.datetime :updated_at, null: false + t.datetime :expires_at + end + create_table :antenna_domains do |t| + t.belongs_to :antenna, null: false, foreign_key: { on_delete: :cascade } + t.string :name, index: true + t.boolean :exclude, null: false, default: false, index: true + t.datetime :created_at, null: false + t.datetime :updated_at, null: false + end + create_table :antenna_tags do |t| + t.belongs_to :antenna, null: false, foreign_key: { on_delete: :cascade } + t.belongs_to :tag, null: false, foreign_key: { on_delete: :cascade } + t.boolean :exclude, null: false, default: false, index: true + t.datetime :created_at, null: false + t.datetime :updated_at, null: false + end + create_table :antenna_accounts do |t| + t.belongs_to :antenna, null: false, foreign_key: { on_delete: :cascade } + t.belongs_to :account, null: false, foreign_key: { on_delete: :cascade } + t.boolean :exclude, null: false, default: false, index: true + t.datetime :created_at, null: false + t.datetime :updated_at, null: false + end + end +end diff --git a/db/schema.rb b/db/schema.rb index fbc856dd5f..3edf4f9183 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 2023_04_20_081634) do +ActiveRecord::Schema.define(version: 2023_04_23_002728) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -253,6 +253,62 @@ ActiveRecord::Schema.define(version: 2023_04_20_081634) do t.bigint "status_ids", array: true end + create_table "antenna_accounts", force: :cascade do |t| + t.bigint "antenna_id", null: false + t.bigint "account_id", null: false + t.boolean "exclude", default: false, null: false + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + t.index ["account_id"], name: "index_antenna_accounts_on_account_id" + t.index ["antenna_id"], name: "index_antenna_accounts_on_antenna_id" + t.index ["exclude"], name: "index_antenna_accounts_on_exclude" + end + + create_table "antenna_domains", force: :cascade do |t| + t.bigint "antenna_id", null: false + t.string "name" + t.boolean "exclude", default: false, null: false + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + t.index ["antenna_id"], name: "index_antenna_domains_on_antenna_id" + t.index ["exclude"], name: "index_antenna_domains_on_exclude" + t.index ["name"], name: "index_antenna_domains_on_name" + end + + create_table "antenna_tags", force: :cascade do |t| + t.bigint "antenna_id", null: false + t.bigint "tag_id", null: false + t.boolean "exclude", default: false, null: false + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + t.index ["antenna_id"], name: "index_antenna_tags_on_antenna_id" + t.index ["exclude"], name: "index_antenna_tags_on_exclude" + t.index ["tag_id"], name: "index_antenna_tags_on_tag_id" + end + + create_table "antennas", force: :cascade do |t| + t.bigint "account_id", null: false + t.bigint "list_id", null: false + t.string "title", default: "", null: false + t.jsonb "keywords" + t.jsonb "exclude_keywords" + t.boolean "any_domains", default: true, null: false + t.boolean "any_tags", default: true, null: false + t.boolean "any_accounts", default: true, null: false + t.boolean "any_keywords", default: true, null: false + t.boolean "available", default: true, null: false + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + t.datetime "expires_at" + t.index ["account_id"], name: "index_antennas_on_account_id" + t.index ["any_accounts"], name: "index_antennas_on_any_accounts" + t.index ["any_domains"], name: "index_antennas_on_any_domains" + t.index ["any_keywords"], name: "index_antennas_on_any_keywords" + t.index ["any_tags"], name: "index_antennas_on_any_tags" + t.index ["available"], name: "index_antennas_on_available" + t.index ["list_id"], name: "index_antennas_on_list_id" + end + create_table "appeals", force: :cascade do |t| t.bigint "account_id", null: false t.bigint "account_warning_id", null: false @@ -1175,6 +1231,13 @@ ActiveRecord::Schema.define(version: 2023_04_20_081634) do add_foreign_key "announcement_reactions", "accounts", on_delete: :cascade add_foreign_key "announcement_reactions", "announcements", on_delete: :cascade add_foreign_key "announcement_reactions", "custom_emojis", on_delete: :cascade + add_foreign_key "antenna_accounts", "accounts", on_delete: :cascade + add_foreign_key "antenna_accounts", "antennas", on_delete: :cascade + add_foreign_key "antenna_domains", "antennas", on_delete: :cascade + add_foreign_key "antenna_tags", "antennas", on_delete: :cascade + add_foreign_key "antenna_tags", "tags", on_delete: :cascade + add_foreign_key "antennas", "accounts", on_delete: :cascade + add_foreign_key "antennas", "lists", on_delete: :cascade add_foreign_key "appeals", "account_warnings", on_delete: :cascade add_foreign_key "appeals", "accounts", column: "approved_by_account_id", on_delete: :nullify add_foreign_key "appeals", "accounts", column: "rejected_by_account_id", on_delete: :nullify