1
0
Fork 0
forked from gitea/nas

Merge branch 'kbtopic_antenna' into kb_migration_development

This commit is contained in:
KMY 2023-04-23 16:46:10 +09:00
commit 63b57de234
21 changed files with 655 additions and 1 deletions

View file

@ -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

200
app/models/antenna.rb Normal file
View file

@ -0,0 +1,200 @@
# == 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 && !expires? #&& !(any_keywords && any_domains && any_accounts && any_tags)
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?
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
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?
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
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
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
end
end

View file

@ -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

View file

@ -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

17
app/models/antenna_tag.rb Normal file
View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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:]_]"

View file

@ -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,31 @@ class FanOutOnWriteService < BaseService
end
end
def deliver_to_antennas!
lists = []
antennas = Antenna.availables
antennas = antennas.left_joins(:antenna_accounts).where(any_accounts: true).or(Antenna.left_joins(:antenna_accounts) .where(antenna_accounts: { exclude: false, account: @status.account }))
antennas = antennas.left_joins(:antenna_domains) .where(any_domains: true) .or(Antenna.left_joins(:antenna_accounts).left_joins(:antenna_domains) .where(antenna_domains: { exclude: false, name: @status.account.domain }))
antennas = antennas.left_joins(:antenna_tags) .where(any_tags: true) .or(Antenna.left_joins(:antenna_accounts).left_joins(:antenna_domains).left_joins(:antenna_tags).where(antenna_tags: { exclude: false, tag: @status.tags }))
antennas.in_batches do |ans|
ans.each do |antenna|
next if !antenna.enabled?
next if antenna.keywords.any? && !antenna.keywords.any? { |keyword| @status.text.include?(keyword) }
next if antenna.exclude_keywords.any? && antenna.exclude_keywords.any? { |keyword| @status.text.include?(keyword) }
next if antenna.antenna_accounts.where(exclude: true, account: @status.account).any?
next if antenna.antenna_domains.where(exclude: true, name: @status.account.domain).any?
next if antenna.antenna_tags.where(exclude: true, tag: @status.tags).any?
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|

View file

@ -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.map { |domain| domain.name }
- 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') }

View file

@ -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 }

View file

@ -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 <tr/>
= link_to_remove_association(f, class: 'table-action-link') do
= safe_join([fa_icon('times'), t('antennas.index.delete')])

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -964,6 +964,27 @@ en:
empty: You have no aliases.
hint_html: If you want to move from another account to this one, here you can create an alias, which is required before you can proceed with moving followers from the old account to this one. This action by itself is <strong>harmless and reversible</strong>. <strong>The account migration is initiated from the old account</strong>.
remove: Unlink alias
antennas:
contexts:
account: Accounts
domain: Domains
keyword: Keywords
tag: Tags
edit:
title: Edit antenna
errors:
deprecated_api_multiple_keywords: These parameters cannot be changed from this application because they apply to more than one filter keyword. Use a more recent application or the web interface.
invalid_context: None or invalid context supplied
index:
contexts: Antennas in %{contexts}
delete: Delete
empty: You have no antennas.
expires_in: Expires in %{distance}
expires_on: Expires on %{date}
title: Antennas
new:
save: Save new antenna
title: Add new antenna
appearance:
advanced_web_interface: Advanced web interface
advanced_web_interface_hint: 'If you want to make use of your entire screen width, the advanced web interface allows you to configure many different columns to see as much information at the same time as you want: Home, notifications, federated timeline, any number of lists and hashtags.'

View file

@ -946,6 +946,32 @@ ja:
empty: エイリアスがありません。
hint_html: 他のアカウントからこのアカウントにフォロワーを引き継いで引っ越したい場合、ここでエイリアスを作成しておく必要があります。エイリアス自体は<strong>無害で、取り消す</strong>ことができます。<strong>引っ越しは以前のアカウント側から開始する必要があります</strong>。
remove: エイリアスを削除
antennas:
contexts:
account: アカウント
domain: ドメイン
keyword: キーワード
tag: ハッシュタグ
edit:
title: アンテナを編集
index:
accounts:
other: "%{count}件のアカウント"
contexts: "%{contexts}のアンテナ"
delete: 削除
domains:
other: "%{count}件のドメイン"
empty: アンテナはありません。
expires_in: "%{distance}で期限切れ"
expires_on: 有効期限 %{date}
keywords:
other: "%{count}件のキーワード"
tags:
other: "%{count}件のタグ"
title: アンテナ
new:
save: 新規アンテナを保存
title: 新規アンテナを追加
appearance:
advanced_web_interface: 上級者向けUI
advanced_web_interface_hint: ディスプレイを幅いっぱいまで活用したい場合、上級者向け UI をおすすめします。ホーム、通知、連合タイムライン、更にはリストやハッシュタグなど、様々な異なるカラムから望む限りの情報を一度に受け取れるような設定が可能になります。

View file

@ -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|

View file

@ -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]

View file

@ -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

View file

@ -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