Add post groups support with new controllers, views, and policies
This commit introduces comprehensive support for post groups across different domains: - Created PostGroupsController to handle viewing post groups - Added new views for displaying post groups and their associated posts - Implemented policies for post groups - Enhanced models to support post group functionality - Updated routes to support post group navigation - Added helper methods for post group interactions - Improved GoodJob argument rendering for post groups The changes provide a unified way to view and interact with post collections across different domains like Inkbunny and E621.
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
rails: RAILS_ENV=development rdbg --command --nonstop --open -- bundle exec rails s -p 3000
|
||||
wp-client: RAILS_ENV=development HMR=true ./bin/webpacker-dev-server
|
||||
wp-server: RAILS_ENV=development HMR=true SERVER_BUNDLE_ONLY=yes ./bin/webpacker --watch
|
||||
css: RAILS_ENV=development yarn "build:css[debug]" --watch
|
||||
css: tailwindcss -c ./config/tailwind.config.js -i ./app/assets/stylesheets/application.tailwind.css -o ./app/assets/builds/tailwind.css --watch
|
||||
prometheus_exporter: RAILS_ENV=development bundle exec prometheus_exporter --bind 0.0.0.0 --prefix redux_ --label '{"environment": "development"}'
|
||||
|
||||
1
TODO.md
1
TODO.md
@@ -14,3 +14,4 @@
|
||||
- [ ] Convert all `state: string` attributes to enums in ActiveRecord models
|
||||
- [ ] Create `belongs_to_log_entry` macro for ActiveRecord models
|
||||
- [ ] Use StaticFileJobHelper for Domain::Fa::Job::ScanFileJob
|
||||
- [ ] Unify HTTP client configs for all domains, so the same job type can be used for different domains
|
||||
|
||||
@@ -3,25 +3,25 @@
|
||||
font-weight: bold;
|
||||
}
|
||||
.ansi-black {
|
||||
color: #000000;
|
||||
color: #333333;
|
||||
}
|
||||
.ansi-red {
|
||||
color: #cd0000;
|
||||
color: #cd3333;
|
||||
}
|
||||
.ansi-green {
|
||||
color: #00cd00;
|
||||
color: #33cd33;
|
||||
}
|
||||
.ansi-yellow {
|
||||
color: #cdcd00;
|
||||
color: #cdcd33;
|
||||
}
|
||||
.ansi-blue {
|
||||
color: #0000ee;
|
||||
color: #3333ee;
|
||||
}
|
||||
.ansi-magenta {
|
||||
color: #cd00cd;
|
||||
color: #cd33cd;
|
||||
}
|
||||
.ansi-cyan {
|
||||
color: #00cdcd;
|
||||
color: #33cdcd;
|
||||
}
|
||||
.ansi-white {
|
||||
color: #e5e5e5;
|
||||
@@ -32,31 +32,31 @@
|
||||
color: #7f7f7f;
|
||||
}
|
||||
.ansi-bright-red {
|
||||
color: #ff0000;
|
||||
color: #990000;
|
||||
}
|
||||
.ansi-bright-green {
|
||||
color: #00ff00;
|
||||
color: #009900;
|
||||
}
|
||||
.ansi-bright-yellow {
|
||||
color: #ffff00;
|
||||
color: #999900;
|
||||
}
|
||||
.ansi-bright-blue {
|
||||
color: #5c5cff;
|
||||
color: #5c5c99;
|
||||
}
|
||||
.ansi-bright-magenta {
|
||||
color: #ff00ff;
|
||||
color: #990099;
|
||||
}
|
||||
.ansi-bright-cyan {
|
||||
color: #00ffff;
|
||||
color: #009999;
|
||||
}
|
||||
.ansi-bright-white {
|
||||
color: #ffffff;
|
||||
color: #999999;
|
||||
}
|
||||
|
||||
.log-uuid {
|
||||
min-width: 20px;
|
||||
max-width: 100px;
|
||||
overflow: hidden;
|
||||
/* white-space: nowrap; */
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
@@ -76,7 +76,8 @@
|
||||
}
|
||||
|
||||
.good-job-execution-log {
|
||||
background: #3d3d3d;
|
||||
color: #333;
|
||||
background: #f0f0f0;
|
||||
}
|
||||
|
||||
.text-truncate-link {
|
||||
|
||||
41
app/controllers/domain/post_groups_controller.rb
Normal file
41
app/controllers/domain/post_groups_controller.rb
Normal file
@@ -0,0 +1,41 @@
|
||||
# typed: true
|
||||
|
||||
class Domain::PostGroupsController < ApplicationController
|
||||
extend T::Sig
|
||||
extend T::Helpers
|
||||
|
||||
skip_before_action :authenticate_user!, only: %i[show]
|
||||
before_action :set_post_group!, only: %i[show]
|
||||
|
||||
# GET /pools/:id
|
||||
sig(:final) { void }
|
||||
def show
|
||||
authorize @post_group
|
||||
end
|
||||
|
||||
sig(:final) do
|
||||
params(param: T.nilable(String)).returns(T.nilable(Domain::PostGroup))
|
||||
end
|
||||
def self.post_group_from_param(param)
|
||||
return nil if param.blank?
|
||||
prefix_param, id = param.split("/")
|
||||
raise("invalid param: #{param}") if prefix_param.blank? || id.blank?
|
||||
group_klass =
|
||||
Domain::PostGroup.subclasses.find do |klass|
|
||||
param_prefix, _ = klass.param_prefix_and_attribute
|
||||
param_prefix == prefix_param
|
||||
end
|
||||
raise("unknown post group type: #{prefix_param}") if group_klass.nil?
|
||||
_, param_attribute = group_klass.param_prefix_and_attribute
|
||||
group_klass.find_by(param_attribute => id)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
sig(:final) { void }
|
||||
def set_post_group!
|
||||
@post_group =
|
||||
Domain::PostGroupsController.post_group_from_param(params[:id]) ||
|
||||
raise(ActiveRecord::RecordNotFound)
|
||||
end
|
||||
end
|
||||
@@ -20,6 +20,7 @@ class Domain::PostsController < ApplicationController
|
||||
@posts = @posts.where(type: postable_types) if postable_types.any?
|
||||
end
|
||||
@posts_index_type_header = "all_posts"
|
||||
@posts_index_show_by = true
|
||||
end
|
||||
|
||||
# GET /posts/:id
|
||||
@@ -45,6 +46,7 @@ class Domain::PostsController < ApplicationController
|
||||
@posts = posts_relation(@user, @user.posts)
|
||||
authorize @posts
|
||||
@posts_index_type_header = "user_created"
|
||||
@posts_index_show_by = false
|
||||
render :index
|
||||
end
|
||||
|
||||
|
||||
17
app/helpers/domain/post_groups_helper.rb
Normal file
17
app/helpers/domain/post_groups_helper.rb
Normal file
@@ -0,0 +1,17 @@
|
||||
# typed: strict
|
||||
module Domain::PostGroupsHelper
|
||||
extend T::Sig
|
||||
extend T::Helpers
|
||||
include HelpersInterface
|
||||
abstract!
|
||||
|
||||
sig { params(post_group: Domain::PostGroup).returns(String) }
|
||||
def domain_post_group_path(post_group)
|
||||
"#{domain_post_groups_path}/#{post_group.to_param}"
|
||||
end
|
||||
|
||||
sig { returns(String) }
|
||||
def domain_post_groups_path
|
||||
"/pools"
|
||||
end
|
||||
end
|
||||
@@ -21,6 +21,11 @@ module Domain::PostsHelper
|
||||
"/posts#{"?#{params_string}" if params_string.present?}"
|
||||
end
|
||||
|
||||
sig { params(post: Domain::Post).returns(String) }
|
||||
def domain_post_post_groups_path(post)
|
||||
"#{domain_post_path(post)}/pools"
|
||||
end
|
||||
|
||||
class DomainData < T::Struct
|
||||
const :domain_icon_path, String
|
||||
const :domain_icon_title, String
|
||||
|
||||
@@ -260,57 +260,59 @@ class Domain::Fa::Job::Base < Scraper::JobBase
|
||||
users_enqueued_for_gallery_scan ||= Set.new
|
||||
users_enqueued_for_favs_scan ||= Set.new
|
||||
|
||||
args =
|
||||
if user.persisted?
|
||||
{ user: user }
|
||||
else
|
||||
unless user.url_name
|
||||
logger.warn format_tags(
|
||||
"no user url_name and is new record, skipping",
|
||||
)
|
||||
return
|
||||
logger.tagged(make_arg_tag(user)) do
|
||||
args =
|
||||
if user.persisted?
|
||||
{ user: user }
|
||||
else
|
||||
unless user.url_name
|
||||
logger.warn format_tags(
|
||||
"no user url_name and is new record, skipping",
|
||||
)
|
||||
return
|
||||
end
|
||||
|
||||
{ url_name: user.url_name }
|
||||
end
|
||||
|
||||
{ url_name: user.url_name }
|
||||
end
|
||||
|
||||
if enqueue_page_scan && users_enqueued_for_page_scan.add?(user.url_name)
|
||||
if user.due_for_page_scan?
|
||||
logger.info(
|
||||
format_tags(
|
||||
"enqueue user page job",
|
||||
make_tag("last scanned", time_ago_in_words(user.scanned_page_at)),
|
||||
),
|
||||
)
|
||||
defer_job(Domain::Fa::Job::UserPageJob, args)
|
||||
end
|
||||
end
|
||||
|
||||
if enqueue_gallery_scan &&
|
||||
users_enqueued_for_gallery_scan.add?(user.url_name)
|
||||
if user.due_for_gallery_scan?
|
||||
logger.info(
|
||||
format_tags(
|
||||
"enqueue user gallery job",
|
||||
make_tag(
|
||||
"last scanned",
|
||||
time_ago_in_words(user.scanned_gallery_at),
|
||||
if enqueue_page_scan && users_enqueued_for_page_scan.add?(user.url_name)
|
||||
if user.due_for_page_scan?
|
||||
logger.info(
|
||||
format_tags(
|
||||
"enqueue user page job",
|
||||
make_tag("last scanned", time_ago_in_words(user.scanned_page_at)),
|
||||
),
|
||||
),
|
||||
)
|
||||
defer_job(Domain::Fa::Job::UserGalleryJob, args)
|
||||
)
|
||||
defer_job(Domain::Fa::Job::UserPageJob, args)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if enqueue_favs_scan && users_enqueued_for_favs_scan.add?(user.url_name)
|
||||
if user.due_for_favs_scan?
|
||||
logger.info(
|
||||
format_tags(
|
||||
"enqueue user favs job",
|
||||
make_tag("last scanned", time_ago_in_words(user.scanned_favs_at)),
|
||||
),
|
||||
)
|
||||
defer_job(Domain::Fa::Job::FavsJob, args)
|
||||
if enqueue_gallery_scan &&
|
||||
users_enqueued_for_gallery_scan.add?(user.url_name)
|
||||
if user.due_for_gallery_scan?
|
||||
logger.info(
|
||||
format_tags(
|
||||
"enqueue user gallery job",
|
||||
make_tag(
|
||||
"last scanned",
|
||||
time_ago_in_words(user.scanned_gallery_at),
|
||||
),
|
||||
),
|
||||
)
|
||||
defer_job(Domain::Fa::Job::UserGalleryJob, args)
|
||||
end
|
||||
end
|
||||
|
||||
if enqueue_favs_scan && users_enqueued_for_favs_scan.add?(user.url_name)
|
||||
if user.due_for_favs_scan?
|
||||
logger.info(
|
||||
format_tags(
|
||||
"enqueue user favs job",
|
||||
make_tag("last scanned", time_ago_in_words(user.scanned_favs_at)),
|
||||
),
|
||||
)
|
||||
defer_job(Domain::Fa::Job::FavsJob, args)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -330,15 +332,16 @@ class Domain::Fa::Job::Base < Scraper::JobBase
|
||||
sig { params(fa_id: Integer, enqueue_pri: T.nilable(Symbol)).void }
|
||||
def enqueue_fa_id_scan(fa_id, enqueue_pri = nil)
|
||||
enqueue_pri = self.class.normalize_enqueue_pri(enqueue_pri)
|
||||
logger.tagged(make_tag("fa_id", fa_id)) do
|
||||
if @posts_enqueued_for_scan.add?(fa_id)
|
||||
logger.info format_tags("enqueue post scan", make_tag("fa_id", fa_id))
|
||||
|
||||
if @posts_enqueued_for_scan.add?(fa_id)
|
||||
logger.info format_tags("enqueue post scan", make_tag("fa_id", fa_id))
|
||||
|
||||
defer_job(
|
||||
Domain::Fa::Job::ScanPostJob,
|
||||
{ fa_id: fa_id },
|
||||
{ priority: enqueue_pri },
|
||||
)
|
||||
defer_job(
|
||||
Domain::Fa::Job::ScanPostJob,
|
||||
{ fa_id: fa_id },
|
||||
{ priority: enqueue_pri },
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -347,31 +350,32 @@ class Domain::Fa::Job::Base < Scraper::JobBase
|
||||
end
|
||||
def enqueue_post_scan(post, enqueue_pri = nil)
|
||||
enqueue_pri = self.class.normalize_enqueue_pri(enqueue_pri)
|
||||
|
||||
if @posts_enqueued_for_scan.add?(T.must(post.fa_id))
|
||||
fa_id_str = (post.fa_id || "(nil)").to_s.bold
|
||||
if !post.scanned_at.present?
|
||||
logger.info format_tags(
|
||||
"enqueue post scan",
|
||||
make_tag("fa_id", fa_id_str),
|
||||
)
|
||||
defer_job(
|
||||
Domain::Fa::Job::ScanPostJob,
|
||||
{ post: post },
|
||||
{ priority: enqueue_pri },
|
||||
)
|
||||
elsif (post_file = post.file) && post_file.url_str.present? &&
|
||||
post_file.log_entry.nil?
|
||||
logger.info format_tags(
|
||||
"enqueue file scan",
|
||||
make_tag("fa_id", fa_id_str),
|
||||
make_tag("post_file.id", post_file.id),
|
||||
)
|
||||
defer_job(
|
||||
Domain::Fa::Job::ScanFileJob,
|
||||
{ post_file: },
|
||||
{ priority: enqueue_pri },
|
||||
)
|
||||
logger.tagged(make_arg_tag(post)) do
|
||||
if @posts_enqueued_for_scan.add?(T.must(post.fa_id))
|
||||
fa_id_str = (post.fa_id || "(nil)").to_s.bold
|
||||
if !post.scanned_at.present?
|
||||
logger.info format_tags(
|
||||
"enqueue post scan",
|
||||
make_tag("fa_id", fa_id_str),
|
||||
)
|
||||
defer_job(
|
||||
Domain::Fa::Job::ScanPostJob,
|
||||
{ post: post },
|
||||
{ priority: enqueue_pri },
|
||||
)
|
||||
elsif (post_file = post.file) && post_file.url_str.present? &&
|
||||
post_file.log_entry.nil?
|
||||
logger.info format_tags(
|
||||
"enqueue file scan",
|
||||
make_tag("fa_id", fa_id_str),
|
||||
make_tag("post_file.id", post_file.id),
|
||||
)
|
||||
defer_job(
|
||||
Domain::Fa::Job::ScanFileJob,
|
||||
{ post_file: },
|
||||
{ priority: enqueue_pri },
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
# typed: strict
|
||||
class Domain::Fa::Job::BrowsePageJob < Domain::Fa::Job::Base
|
||||
queue_as :fa_browse_page
|
||||
MAX_PAGE_NUMBER = T.let(Rails.env.development? ? 2 : 150, Integer)
|
||||
|
||||
sig { params(args: T.untyped).void }
|
||||
def initialize(*args)
|
||||
@@ -14,17 +15,27 @@ class Domain::Fa::Job::BrowsePageJob < Domain::Fa::Job::Base
|
||||
def perform(args)
|
||||
while true
|
||||
break unless scan_browse_page
|
||||
break if @page_number > 150
|
||||
|
||||
if @page_number > MAX_PAGE_NUMBER
|
||||
logger.warn(
|
||||
format_tags(
|
||||
make_tag("page_number", @page_number),
|
||||
make_tag("max_page_number", MAX_PAGE_NUMBER),
|
||||
"exceeded max page number",
|
||||
),
|
||||
)
|
||||
break
|
||||
end
|
||||
@page_number += 1
|
||||
end
|
||||
|
||||
logger.info(
|
||||
[
|
||||
"[finished]",
|
||||
"[total new: #{@total_num_new_posts_seen.to_s.bold}]",
|
||||
"[total seen: #{@total_num_posts_seen.to_s.bold}]",
|
||||
"[pages: #{@page_number.to_s.bold}]",
|
||||
].join(" "),
|
||||
format_tags(
|
||||
make_tag("total new posts", @total_num_new_posts_seen),
|
||||
make_tag("total seen posts", @total_num_posts_seen),
|
||||
make_tag("pages", @page_number),
|
||||
"finished",
|
||||
),
|
||||
)
|
||||
end
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ class Domain::Fa::Job::ScanFileJob < Domain::Fa::Job::Base
|
||||
sig { override.params(args: T::Hash[Symbol, T.untyped]).returns(T.untyped) }
|
||||
def perform(args)
|
||||
file =
|
||||
T.cast(args[:post_file], T.nilable(Domain::PostFile)) ||
|
||||
T.cast(args[:file] || args[:post_file], T.nilable(Domain::PostFile)) ||
|
||||
begin
|
||||
post = args[:post]
|
||||
if post.nil?
|
||||
|
||||
@@ -7,6 +7,8 @@ class Domain::Fa::Job::ScanPostJob < Domain::Fa::Job::Base
|
||||
post = post_from_args!(build_post: true)
|
||||
logger.push_tags(make_arg_tag(post))
|
||||
|
||||
logger.info("scanning post")
|
||||
|
||||
if (post.state == "ok" && !post.scanned_at.present?) || force_scan?
|
||||
ReduxApplicationRecord.transaction { scan_post(post) }
|
||||
end
|
||||
@@ -14,14 +16,18 @@ class Domain::Fa::Job::ScanPostJob < Domain::Fa::Job::Base
|
||||
file = post.file
|
||||
logger.push_tags(make_arg_tag(file)) if file.present?
|
||||
|
||||
if (file.present? && file.state == "pending" && file.url_str.present?) ||
|
||||
force_scan?
|
||||
if force_scan? ||
|
||||
(file.present? && (file.state == "pending") && file.url_str.present?)
|
||||
logger.info(
|
||||
format_tags("enqueue file job", make_tag("priority", self.priority)),
|
||||
format_tags(
|
||||
"enqueue file job",
|
||||
make_tag("url", file&.url_str),
|
||||
make_tag("priority", self.priority),
|
||||
),
|
||||
)
|
||||
defer_job(
|
||||
Domain::Fa::Job::ScanFileJob,
|
||||
{ post_file: post.file },
|
||||
{ file: post.file },
|
||||
{ priority: self.priority },
|
||||
)
|
||||
end
|
||||
@@ -37,6 +43,8 @@ class Domain::Fa::Job::ScanPostJob < Domain::Fa::Job::Base
|
||||
suppress_jobs: [{ job: self.class, fa_id: post.fa_id }],
|
||||
)
|
||||
end
|
||||
|
||||
post.scanned_at = DateTime.current
|
||||
logger.info format_tags("finished post scan")
|
||||
ensure
|
||||
post.save! if post
|
||||
@@ -48,8 +56,11 @@ class Domain::Fa::Job::ScanPostJob < Domain::Fa::Job::Base
|
||||
def scan_post(post)
|
||||
response =
|
||||
http_client.get("https://www.furaffinity.net/view/#{post.fa_id}/")
|
||||
post.last_submission_log_entry = response.log_entry
|
||||
|
||||
if response.status_code == 404
|
||||
post.state = "scan_error"
|
||||
logger.error(format_tags("post does not exist"))
|
||||
return
|
||||
end
|
||||
|
||||
@@ -87,12 +98,12 @@ class Domain::Fa::Job::ScanPostJob < Domain::Fa::Job::Base
|
||||
|
||||
# save before any changes so post has an id for any files
|
||||
post.save!
|
||||
post.last_submission_log_entry = first_log_entry
|
||||
|
||||
post.title = submission.title
|
||||
creator =
|
||||
Domain::User::FaUser.find_or_build_from_submission_parser(submission)
|
||||
creator.save!
|
||||
post.create_primary_user_post_creation!(user: creator)
|
||||
post.creator = creator
|
||||
post.category = submission.category
|
||||
post.description =
|
||||
submission.description_html.encode(
|
||||
@@ -103,7 +114,9 @@ class Domain::Fa::Job::ScanPostJob < Domain::Fa::Job::Base
|
||||
post.keywords = submission.keywords_array || []
|
||||
uri = Addressable::URI.parse(submission.full_res_img)
|
||||
uri.scheme = "https" if uri.scheme.blank?
|
||||
post.files.create!(url_str: uri.to_s)
|
||||
file = post.file || post.build_file
|
||||
file.url_str = uri.to_s
|
||||
file.save!
|
||||
post.theme = submission.theme
|
||||
post.species = submission.species
|
||||
post.gender = submission.gender
|
||||
|
||||
@@ -9,7 +9,7 @@ class Domain::Inkbunny::Job::ApiSearchPageProcessor
|
||||
include HasColorLogger
|
||||
|
||||
SUBMISSIONS_PER_PAGE = 100
|
||||
MAX_LOOP_COUNT = 50
|
||||
MAX_PAGE_COUNT = T.let(Rails.env.development? ? 2 : 50, Integer)
|
||||
|
||||
sig { returns(T::Array[Domain::Post::InkbunnyPost]) }
|
||||
attr_reader :changed_posts
|
||||
|
||||
@@ -15,6 +15,16 @@ class Domain::Inkbunny::Job::Base < Scraper::JobBase
|
||||
T.cast(arguments[0][:user], T.nilable(Domain::User::InkbunnyUser))
|
||||
end
|
||||
|
||||
sig { returns(Domain::UserAvatar) }
|
||||
def avatar_from_args!
|
||||
T.cast(arguments[0][:avatar], Domain::UserAvatar)
|
||||
end
|
||||
|
||||
sig { returns(T.nilable(Domain::UserAvatar)) }
|
||||
def avatar_from_args
|
||||
T.cast(arguments[0][:avatar], T.nilable(Domain::UserAvatar))
|
||||
end
|
||||
|
||||
sig { returns(Domain::User::InkbunnyUser) }
|
||||
def user_from_args!
|
||||
user_from_args || raise("user must exist")
|
||||
|
||||
@@ -11,8 +11,8 @@ module Domain::Inkbunny::Job
|
||||
|
||||
while true
|
||||
loop_count += 1
|
||||
if loop_count > ApiSearchPageProcessor::MAX_LOOP_COUNT
|
||||
raise("loop_count: #{loop_count}")
|
||||
if loop_count > ApiSearchPageProcessor::MAX_PAGE_COUNT
|
||||
fatal_error("loop_count > #{ApiSearchPageProcessor::MAX_PAGE_COUNT}")
|
||||
end
|
||||
|
||||
url = ApiSearchPageProcessor.build_api_search_url(rid: rid, page: page)
|
||||
|
||||
@@ -29,8 +29,8 @@ module Domain::Inkbunny::Job
|
||||
|
||||
while true
|
||||
loop_count += 1
|
||||
if loop_count > ApiSearchPageProcessor::MAX_LOOP_COUNT
|
||||
fatal_error("loop_count > #{ApiSearchPageProcessor::MAX_LOOP_COUNT}")
|
||||
if loop_count > ApiSearchPageProcessor::MAX_PAGE_COUNT
|
||||
fatal_error("loop_count > #{ApiSearchPageProcessor::MAX_PAGE_COUNT}")
|
||||
end
|
||||
|
||||
url =
|
||||
|
||||
@@ -59,7 +59,7 @@ class Domain::Inkbunny::Job::UpdatePostsJob < Domain::Inkbunny::Job::Base
|
||||
)
|
||||
url = build_api_submissions_url(ib_post_ids_chunk)
|
||||
response = http_client.get(url)
|
||||
logger.tagged(make_arg_tag(response, name: "submissions_hle")) do
|
||||
logger.tagged(make_arg_tag(response.log_entry, name: "submissions_hle")) do
|
||||
if response.status_code != 200
|
||||
fatal_error("api_submissions failed: #{response.status_code}")
|
||||
end
|
||||
@@ -262,7 +262,7 @@ class Domain::Inkbunny::Job::UpdatePostsJob < Domain::Inkbunny::Job::Base
|
||||
pool =
|
||||
Domain::PostGroup::InkbunnyPool.find_or_initialize_by(
|
||||
ib_id: pool_json["pool_id"],
|
||||
)
|
||||
) { |pool| pool.owner = post.creator }
|
||||
pools_to_update.add(pool) if pool.deep_update_log_entry_id.blank?
|
||||
pool.count = pool_json["count"]&.to_i
|
||||
pool.name = pool_json["name"]
|
||||
|
||||
@@ -5,9 +5,8 @@ module Domain::Inkbunny::Job
|
||||
|
||||
sig { override.params(args: T::Hash[Symbol, T.untyped]).returns(T.untyped) }
|
||||
def perform(args)
|
||||
user = user_from_args!
|
||||
avatar = T.must(user.avatar)
|
||||
logger.push_tags(make_arg_tag(user), make_arg_tag(avatar))
|
||||
avatar = avatar_from_args!
|
||||
logger.push_tags(make_arg_tag(avatar))
|
||||
|
||||
url_str = avatar.url_str
|
||||
if url_str.blank?
|
||||
|
||||
@@ -326,15 +326,19 @@ class Scraper::JobBase < ApplicationJob
|
||||
ColorLogger.log_line_accumulator = proc { |line| log_lines << line }
|
||||
block.call
|
||||
ensure
|
||||
ColorLogger.log_line_accumulator = nil
|
||||
job.enqueue_deferred_jobs!
|
||||
log_lines = T.must(log_lines)
|
||||
good_job = GoodJob::CurrentThread.job
|
||||
last_execution = Scraper::JobBase.last_good_job_execution
|
||||
if good_job && last_execution && good_job == last_execution.job &&
|
||||
log_lines.any?
|
||||
Scraper::JobBase.last_good_job_execution = nil
|
||||
last_execution.create_log_lines_collection!(log_lines: log_lines)
|
||||
begin
|
||||
job.enqueue_deferred_jobs!
|
||||
ColorLogger.log_line_accumulator = nil
|
||||
log_lines = T.must(log_lines)
|
||||
good_job = GoodJob::CurrentThread.job
|
||||
last_execution = Scraper::JobBase.last_good_job_execution
|
||||
if good_job && last_execution && good_job == last_execution.job &&
|
||||
log_lines.any?
|
||||
Scraper::JobBase.last_good_job_execution = nil
|
||||
last_execution.create_log_lines_collection!(log_lines: log_lines)
|
||||
end
|
||||
rescue StandardError => e
|
||||
logger.error("error collecting log lines: #{e.class.name} - #{e.message}")
|
||||
end
|
||||
end
|
||||
|
||||
@@ -377,14 +381,18 @@ class Scraper::JobBase < ApplicationJob
|
||||
source_class: self.class,
|
||||
enqueued_class: deferred_job.job_class,
|
||||
)
|
||||
logger.info(
|
||||
format_tags(
|
||||
make_tag("job_class", deferred_job.job_class.name),
|
||||
make_tag("job_id", job.job_id),
|
||||
"enqueue deferred job",
|
||||
),
|
||||
)
|
||||
if job
|
||||
logger.info(
|
||||
format_tags(
|
||||
make_tag("job_class", deferred_job.job_class.name),
|
||||
(make_tag("job_id", job.job_id)),
|
||||
"enqueue deferred job",
|
||||
),
|
||||
)
|
||||
end
|
||||
end
|
||||
rescue StandardError => e
|
||||
logger.error("error enqueueing jobs: #{e.class.name} - #{e.message}")
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -52,9 +52,7 @@ class Scraper::ClientFactory
|
||||
if Rails.env.test?
|
||||
@http_client_mock || raise("no http client mock set")
|
||||
else
|
||||
client = _http_client_impl(:inkbunny, Scraper::InkbunnyHttpClientConfig)
|
||||
sid = _get_ib_client_sid(client)
|
||||
client
|
||||
_http_client_impl(:inkbunny, Scraper::InkbunnyHttpClientConfig)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -77,24 +75,7 @@ class Scraper::ClientFactory
|
||||
|
||||
def self._http_client_impl(key, config_klass)
|
||||
@http_clients.value[key] ||= begin
|
||||
# proxy_config = Rails.application.config.x.proxy || raise("no proxy config")
|
||||
# performer = Scraper::HttpPerformer.new(proxy_config[:name], proxy_config[:http])
|
||||
performer = Scraper::CurlHttpPerformer.new
|
||||
Scraper::HttpClient.new(config_klass.new, performer)
|
||||
Scraper::HttpClient.new(config_klass.new, Scraper::CurlHttpPerformer.new)
|
||||
end
|
||||
end
|
||||
|
||||
def self._get_ib_client_sid(client)
|
||||
sid = GlobalState.find_or_create_by!(key: "inkbunny-sid")
|
||||
sid.with_lock("FOR UPDATE") do
|
||||
if sid.value.blank?
|
||||
sid.value = _ib_login(client)
|
||||
sid.save!
|
||||
end
|
||||
end
|
||||
sid.value
|
||||
end
|
||||
|
||||
def self._ib_login(client)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -47,7 +47,7 @@ class Domain::Post::FaPost < Domain::Post
|
||||
|
||||
sig { override.returns(T.nilable(String)) }
|
||||
def title
|
||||
super
|
||||
super || "(unknown)"
|
||||
end
|
||||
|
||||
sig { override.returns(T.nilable(Domain::User)) }
|
||||
|
||||
@@ -95,6 +95,11 @@ class Domain::Post::InkbunnyPost < Domain::Post
|
||||
self.creator
|
||||
end
|
||||
|
||||
sig { override.returns(T.nilable(HttpLogEntry)) }
|
||||
def scanned_post_log_entry_for_view
|
||||
self.deep_update_log_entry
|
||||
end
|
||||
|
||||
sig { override.returns(T.nilable(T.any(String, Integer))) }
|
||||
def domain_id_for_view
|
||||
self.ib_id
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
class Domain::PostGroup < ReduxApplicationRecord
|
||||
extend T::Helpers
|
||||
include AttrJsonRecordAliases
|
||||
include HasCompositeToParam
|
||||
self.table_name = "domain_post_groups"
|
||||
abstract!
|
||||
|
||||
@@ -10,4 +11,9 @@ class Domain::PostGroup < ReduxApplicationRecord
|
||||
inverse_of: :group,
|
||||
dependent: :destroy
|
||||
has_many :posts, through: :post_group_joins, source: :post
|
||||
|
||||
sig { overridable.returns(T.nilable(String)) }
|
||||
def external_url_for_view
|
||||
nil
|
||||
end
|
||||
end
|
||||
|
||||
@@ -2,4 +2,9 @@
|
||||
class Domain::PostGroup::E621Pool < Domain::PostGroup
|
||||
attr_json :e621_id, :integer
|
||||
validates :e621_id, presence: true
|
||||
|
||||
sig { override.returns([String, Symbol]) }
|
||||
def self.param_prefix_and_attribute
|
||||
["e621", :e621_id]
|
||||
end
|
||||
end
|
||||
|
||||
@@ -5,10 +5,24 @@ class Domain::PostGroup::InkbunnyPool < Domain::PostGroup
|
||||
attr_json :count, :integer
|
||||
attr_json :name, :string
|
||||
attr_json :description, :string
|
||||
|
||||
attr_json :owner_id, :integer
|
||||
validates :ib_id, presence: true
|
||||
|
||||
belongs_to :deep_update_log_entry,
|
||||
class_name: "::HttpLogEntry",
|
||||
optional: true
|
||||
|
||||
belongs_to :owner, class_name: "::Domain::User::InkbunnyUser", optional: true
|
||||
|
||||
sig { override.returns([String, Symbol]) }
|
||||
def self.param_prefix_and_attribute
|
||||
["ib", :ib_id]
|
||||
end
|
||||
|
||||
sig { override.returns(T.nilable(String)) }
|
||||
def external_url_for_view
|
||||
if ib_id = self.ib_id
|
||||
"https://inkbunny.net/submissionsviewall.php?pool_id=#{self.ib_id}"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -69,9 +69,11 @@ class ReduxApplicationRecord < ActiveRecord::Base
|
||||
.job_class
|
||||
.set(deferred_job.set_args)
|
||||
.perform_later(deferred_job.params)
|
||||
logger.info(
|
||||
"[class: #{self.class.name}][id: #{id}][enqueued job: #{deferred_job.job_class.name}][job_id: #{job.job_id}]",
|
||||
)
|
||||
if job
|
||||
logger.info(
|
||||
"[class: #{self.class.name}][id: #{id}][enqueued job: #{deferred_job.job_class.name}][job_id: #{job.job_id}]",
|
||||
)
|
||||
end
|
||||
Scraper::Metrics::JobBaseMetrics.observe_job_enqueued(
|
||||
source_class: self.class,
|
||||
enqueued_class: deferred_job.job_class,
|
||||
|
||||
3
app/policies/domain/post_group/e621_pool_policy.rb
Normal file
3
app/policies/domain/post_group/e621_pool_policy.rb
Normal file
@@ -0,0 +1,3 @@
|
||||
# typed: strict
|
||||
class Domain::PostGroup::E621PoolPolicy < Domain::PostGroupPolicy
|
||||
end
|
||||
3
app/policies/domain/post_group/inkbunny_pool_policy.rb
Normal file
3
app/policies/domain/post_group/inkbunny_pool_policy.rb
Normal file
@@ -0,0 +1,3 @@
|
||||
# typed: strict
|
||||
class Domain::PostGroup::InkbunnyPoolPolicy < Domain::PostGroupPolicy
|
||||
end
|
||||
9
app/policies/domain/post_group_policy.rb
Normal file
9
app/policies/domain/post_group_policy.rb
Normal file
@@ -0,0 +1,9 @@
|
||||
# typed: strict
|
||||
class Domain::PostGroupPolicy < ApplicationPolicy
|
||||
extend T::Sig
|
||||
|
||||
sig { returns(T::Boolean) }
|
||||
def show?
|
||||
true
|
||||
end
|
||||
end
|
||||
75
app/views/domain/post_groups/show.html.erb
Normal file
75
app/views/domain/post_groups/show.html.erb
Normal file
@@ -0,0 +1,75 @@
|
||||
<div class="mx-auto mt-4 flex w-full max-w-2xl flex-col gap-4 pb-4">
|
||||
<section class="rounded-md border border-slate-300 bg-slate-50 p-4">
|
||||
<div class="flex items-center gap-4">
|
||||
<div class="flex-grow min-w-0 items-center gap-4">
|
||||
<span class="truncate text-lg font-medium">
|
||||
<% if url = @post_group.external_url_for_view %>
|
||||
<%= link_to url, target: "_blank", rel: "noopener noreferrer", class: "flex items-center gap-1 text-blue-600 hover:underline" do %>
|
||||
<%= @post_group.name %>
|
||||
<%= render partial: "shared/icons/external_link", locals: { class_name: "w-4 h-4" } %>
|
||||
<% end %>
|
||||
<% else %>
|
||||
<%= @post_group.name %>
|
||||
<% end %>
|
||||
</span>
|
||||
<div class="text-slate-600">
|
||||
<%= pluralize(@post_group.posts.count, "post") %>
|
||||
</div>
|
||||
</div>
|
||||
<% if @post_group.respond_to?(:owner) && (owner = @post_group.owner) %>
|
||||
<%= render partial: "domain/users/inline_link", locals: { user: owner, with_post_count: false } %>
|
||||
<% end %>
|
||||
</div>
|
||||
</section>
|
||||
<div class="mx-auto flex flex-wrap justify-center">
|
||||
<% @post_group.posts.each do |post| %>
|
||||
<div class="m-4 flex h-fit flex-col rounded-lg border border-slate-300 bg-slate-50 shadow-sm">
|
||||
<div class="flex justify-between border-b border-slate-300 p-4">
|
||||
<div>
|
||||
<%= render partial: "domain/posts/inline_postable_domain_link", locals: { post: post } %>
|
||||
</div>
|
||||
<div>
|
||||
<% if creator = post.primary_creator_for_view %>
|
||||
<%= link_to creator.name_for_view,
|
||||
domain_user_path(creator),
|
||||
class: "text-blue-600 hover:text-blue-800" %>
|
||||
<% elsif fallback = post.primary_creator_name_fallback_for_view %>
|
||||
<%= fallback %>
|
||||
<% else %>
|
||||
(nobody)
|
||||
<% end %>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex items-center justify-center p-4">
|
||||
<% if thumbnail_file = gallery_file_for_post(post) %>
|
||||
<%= link_to domain_post_path(post) do %>
|
||||
<%= image_tag blob_path(
|
||||
HexUtil.bin2hex(thumbnail_file.log_entry.response_sha256),
|
||||
format: "jpg",
|
||||
thumb: "small",
|
||||
),
|
||||
class: "max-h-[300px] max-w-[300px] rounded-md border border-slate-300 object-contain shadow-md",
|
||||
alt: post.title %>
|
||||
<% end %>
|
||||
<% else %>
|
||||
<span>No file available</span>
|
||||
<% end %>
|
||||
</div>
|
||||
<div class="border-t border-slate-300">
|
||||
<h2 class="p-4 text-center text-lg">
|
||||
<%= link_to post.title, domain_post_path(post), class: "sky-link" %>
|
||||
</h2>
|
||||
<div class="px-4 pb-4 text-sm text-slate-500">
|
||||
<div class="flex justify-end">
|
||||
<% if post.posted_at %>
|
||||
Posted <%= time_ago_in_words(post.posted_at) %> ago
|
||||
<% else %>
|
||||
Post date unknown
|
||||
<% end %>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<% end %>
|
||||
</div>
|
||||
</div>
|
||||
@@ -40,12 +40,17 @@
|
||||
<%= link_to post.title, domain_post_path(post), class: "sky-link" %>
|
||||
</h2>
|
||||
<div class="px-4 pb-4 text-sm text-slate-500">
|
||||
<div class="flex justify-end">
|
||||
<% if post.posted_at %>
|
||||
Posted <%= time_ago_in_words(post.posted_at) %> ago
|
||||
<% else %>
|
||||
Post date unknown
|
||||
<% end %>
|
||||
<div class="flex justify-between gap-2">
|
||||
<span>
|
||||
<% if @posts_index_show_by && (creator = post.primary_creator_for_view) %>
|
||||
by <%= link_to creator.name_for_view, domain_user_path(creator), class: "sky-link" %>
|
||||
<% end %>
|
||||
</span>
|
||||
<span>
|
||||
<% if post.posted_at %>
|
||||
Posted <%= time_ago_in_words(post.posted_at) %> ago
|
||||
<% end %>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
16
app/views/domain/posts/default/_section_post_groups.html.erb
Normal file
16
app/views/domain/posts/default/_section_post_groups.html.erb
Normal file
@@ -0,0 +1,16 @@
|
||||
<% return unless post.respond_to?(:pools) %>
|
||||
<% pools = post.pools %>
|
||||
<% return if pools.empty? %>
|
||||
<section class="sky-section">
|
||||
<div class="section-header">Post Pools</div>
|
||||
<div class="grid grid-cols-[1fr] divide-y divide-slate-300 bg-slate-100">
|
||||
<% pools.each do |pool| %>
|
||||
<div class="flex items-center justify-between px-4 py-2">
|
||||
<%= link_to pool.name,
|
||||
domain_post_group_path(pool),
|
||||
class: "text-blue-600 hover:underline" %>
|
||||
<span class="text-slate-600"><%= pool.posts.count %> posts</span>
|
||||
</div>
|
||||
<% end %>
|
||||
</div>
|
||||
</section>
|
||||
@@ -1,16 +1,23 @@
|
||||
<% if policy(post).view_file? %>
|
||||
<section>
|
||||
<% if (log_entry = post.primary_file_for_view&.log_entry) && log_entry.status_code == 200 %>
|
||||
<%= render partial: "log_entries/content_container",
|
||||
<% if (log_entry = post.primary_file_for_view&.log_entry) %>
|
||||
<% if log_entry.status_code == 200 %>
|
||||
<%= render partial: "log_entries/content_container",
|
||||
locals: {
|
||||
log_entry: log_entry,
|
||||
} %>
|
||||
<% else %>
|
||||
<section class="flex grow justify-center overflow-clip">
|
||||
<% else %>
|
||||
<div class="text-slate-500">
|
||||
<i class="fa-solid fa-exclamation-triangle"></i>
|
||||
File error
|
||||
</div>
|
||||
<% end %>
|
||||
<% else %>
|
||||
<section class="flex grow justify-center overflow-clip">
|
||||
<div class="text-slate-500">
|
||||
<i class="fa-solid fa-file-arrow-down"></i>
|
||||
No file
|
||||
</div>
|
||||
</section>
|
||||
<% end %>
|
||||
</section>
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
<div class="mx-auto mt-4 flex w-full max-w-2xl flex-col gap-4 pb-4">
|
||||
<%= render_for_model(@post, "section_post_title", as: :post) %>
|
||||
<%= render_for_model(@post, "section_post_groups", as: :post) %>
|
||||
<%= render_for_model(@post, "section_primary_file", as: :post) %>
|
||||
<%= render_for_model(@post, "section_description", as: :post) %>
|
||||
<%= render_for_model(@post, "section_tags", as: :post) %>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<div class="flex w-full items-center">
|
||||
<div class="flex items-center">
|
||||
<a class="flex grow items-center gap-2" href="<%= domain_user_path(user) %>">
|
||||
<img
|
||||
src="<%= domain_user_avatar_img_src_path(user.avatar, thumb: "64-avatar") %>"
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
<% fav_posts = user.faved_posts.limit(5).to_a %>
|
||||
<section class="animated-shadow-sky sky-section">
|
||||
<h2 class="section-header">
|
||||
<span class="font-medium text-slate-900">Favorited Posts</span>
|
||||
<span class="float-right">
|
||||
<%= link_to "#{user.faved_posts.count} total", domain_user_favorites_path(user), class: "sky-link" %>
|
||||
</span>
|
||||
</h2>
|
||||
<% if fav_posts.any? %>
|
||||
<% fav_posts.each do |post| %>
|
||||
<div class="flex items-center px-4 py-2">
|
||||
<span class="grow truncate">
|
||||
<%= link_to post.title, domain_post_path(post), class: "sky-link block truncate" %>
|
||||
<% if creator = post.primary_creator_for_view %>
|
||||
<span class="text-slate-500 text-sm">
|
||||
by <%= link_to creator.name, domain_user_path(creator), class: "sky-link" %>
|
||||
</span>
|
||||
<% end %>
|
||||
</span>
|
||||
<span class="whitespace-nowrap text-slate-500">
|
||||
<% if posted_at = post.posted_at %>
|
||||
<%= time_ago_in_words(posted_at) %> ago
|
||||
<% else %>
|
||||
unknown
|
||||
<% end %>
|
||||
</span>
|
||||
</div>
|
||||
<% end %>
|
||||
<% else %>
|
||||
<div class="px-4 py-3 text-slate-500">No posts</div>
|
||||
<% end %>
|
||||
</section>
|
||||
|
||||
@@ -1,27 +0,0 @@
|
||||
<% fav_posts = user.faved_posts.limit(5).to_a %>
|
||||
<section class="animated-shadow-sky sky-section">
|
||||
<h2 class="section-header">
|
||||
<span class="font-medium text-slate-900">Favorited Posts</span>
|
||||
<span class="float-right">
|
||||
<%= link_to "#{user.faved_posts.count} total", domain_user_favorites_path(user), class: "sky-link" %>
|
||||
</span>
|
||||
</h2>
|
||||
<% if fav_posts.any? %>
|
||||
<% fav_posts.each do |post| %>
|
||||
<div class="flex items-center px-4 py-2">
|
||||
<span class="grow truncate">
|
||||
<%= link_to post.title, domain_post_path(post), class: "sky-link block truncate" %>
|
||||
</span>
|
||||
<span class="whitespace-nowrap text-slate-500">
|
||||
<% if posted_at = post.posted_at %>
|
||||
<%= time_ago_in_words(posted_at) %> ago
|
||||
<% else %>
|
||||
unknown
|
||||
<% end %>
|
||||
</span>
|
||||
</div>
|
||||
<% end %>
|
||||
<% else %>
|
||||
<div class="px-4 py-3 text-slate-500">No posts found</div>
|
||||
<% end %>
|
||||
</section>
|
||||
@@ -4,10 +4,11 @@
|
||||
<div class="w-full sm:w-1/2">
|
||||
<%= render_for_model @user, "stats", as: :user %>
|
||||
</div>
|
||||
<div class="w-full sm:w-1/2">
|
||||
<div class="flex flex-col gap-4 w-full sm:w-1/2">
|
||||
<% if @user.has_created_posts? %>
|
||||
<%= render_for_model @user, "recent_created_posts", as: :user %>
|
||||
<% elsif @user.has_faved_posts? %>
|
||||
<% end %>
|
||||
<% if @user.has_faved_posts? %>
|
||||
<%= render_for_model @user, "recent_faved_posts", as: :user %>
|
||||
<% end %>
|
||||
</div>
|
||||
|
||||
@@ -26,6 +26,10 @@
|
||||
<%= render "good_job/arguments/domain_e621_post", post: value %>
|
||||
<% when Domain::PostFile %>
|
||||
<%= render "good_job/arguments/domain_post_file", post_file: value %>
|
||||
<% when Domain::Post %>
|
||||
<%= render "good_job/arguments/domain_post", post: value %>
|
||||
<% when Domain::User %>
|
||||
<%= render "good_job/arguments/domain_user", user: value %>
|
||||
<% else %>
|
||||
<div class="text-truncate">
|
||||
<code class="small" title="<%= value.inspect %>"
|
||||
|
||||
72
app/views/good_job/arguments/_domain_post.html.erb
Normal file
72
app/views/good_job/arguments/_domain_post.html.erb
Normal file
@@ -0,0 +1,72 @@
|
||||
<%# Display post information with associated details %>
|
||||
<div class="d-flex align-items-center gap-2">
|
||||
<div class="d-flex align-items-center gap-2">
|
||||
<%= link_to domain_post_path(post),
|
||||
class: "badge bg-primary",
|
||||
target: "_blank" do %>
|
||||
<i class="fa-solid fa-image me-1"></i><%= post.class.name %> #<%= post.id %>
|
||||
<% end %>
|
||||
<% attr = post.class.param_prefix_and_attribute[1]%>
|
||||
<span class="badge bg-secondary text-truncate" title="<%= post.send(attr) %>">
|
||||
<i class="fa-solid fa-tag me-1"></i><%= attr %>:<%= post.send(attr) %>
|
||||
</span>
|
||||
<% if post.title.present? %>
|
||||
<span class="badge bg-secondary text-truncate" title="<%= post.title %>">
|
||||
<i class="fa-solid fa-heading me-1"></i><%= post.title %>
|
||||
</span>
|
||||
<% end %>
|
||||
</div>
|
||||
<div class="d-flex align-items-center ms-auto gap-2">
|
||||
<% if post.domain_url_for_view.present? %>
|
||||
<%= link_to post.domain_url_for_view.to_s,
|
||||
class: "badge bg-secondary text-truncate-link",
|
||||
target: "_blank",
|
||||
rel: "noopener noreferrer nofollow" do %>
|
||||
<i class="fa-solid fa-link me-1"></i><%= post.domain_abbreviation_for_view %>
|
||||
<% end %>
|
||||
<% end %>
|
||||
<% if post.primary_creator_for_view.present? %>
|
||||
<span class="badge bg-info">
|
||||
<i class="fa-solid fa-user me-1"></i><%= post.primary_creator_for_view.name %>
|
||||
</span>
|
||||
<% elsif post.primary_creator_name_fallback_for_view.present? %>
|
||||
<span class="badge bg-info">
|
||||
<i class="fa-solid fa-user me-1"></i><%= post.primary_creator_name_fallback_for_view %>
|
||||
</span>
|
||||
<% end %>
|
||||
<% if post.num_favorites_for_view.present? %>
|
||||
<span class="badge bg-light text-dark">
|
||||
<i class="fa-solid fa-star me-1"></i><%= post.num_favorites_for_view %>
|
||||
</span>
|
||||
<% end %>
|
||||
<% if log_entry = post.scanned_post_log_entry_for_view %>
|
||||
<%= link_to Rails.application.routes.url_helpers.log_entry_path(log_entry),
|
||||
class: "badge bg-secondary",
|
||||
target: "_blank" do %>
|
||||
<i class="fa-solid fa-file-lines me-1"></i>HttpLogEntry #<%= log_entry.id %>
|
||||
<% end %>
|
||||
<% end %>
|
||||
<% if post.posted_at.present? %>
|
||||
<span class="badge bg-light text-dark" title="<%= time_ago_in_words(post.posted_at) %> ago">
|
||||
<i class="fa-regular fa-clock me-1"></i><%= post.posted_at.strftime("%Y-%m-%d %H:%M:%S") %>
|
||||
</span>
|
||||
<% end %>
|
||||
<% if post.file_downloaded_at.present? %>
|
||||
<span class="badge bg-success">
|
||||
<i class="fa-solid fa-download me-1"></i>Downloaded
|
||||
</span>
|
||||
<% end %>
|
||||
<% if primary_file = post.primary_file_for_view %>
|
||||
<% if primary_file.blob.present? %>
|
||||
<span class="badge bg-light text-dark">
|
||||
<i class="fa-solid fa-file-arrow-down me-1"></i>SHA256: <%= HexUtil.bin2hex(primary_file.blob_sha256[0..7]) %>
|
||||
</span>
|
||||
<% end %>
|
||||
<% end %>
|
||||
</div>
|
||||
</div>
|
||||
<% if post.description_for_view.present? %>
|
||||
<div class="mt-2">
|
||||
<small class="text-muted"><%= truncate(post.description_for_view, length: 200) %></small>
|
||||
</div>
|
||||
<% end %>
|
||||
43
app/views/good_job/arguments/_domain_user.html.erb
Normal file
43
app/views/good_job/arguments/_domain_user.html.erb
Normal file
@@ -0,0 +1,43 @@
|
||||
<%# Display user information with associated details %>
|
||||
<div class="d-flex align-items-center gap-2">
|
||||
<div class="d-flex align-items-center gap-2">
|
||||
<% attr = user.class.param_prefix_and_attribute[1] %>
|
||||
<%= link_to domain_user_path(user),
|
||||
class: "badge bg-primary",
|
||||
target: "_blank" do %>
|
||||
<i class="fa-solid fa-user me-1"></i><%= user.class.name %> #<%= user.id %>
|
||||
<% end %>
|
||||
<span class="badge bg-secondary text-truncate" title="<%= user.send(attr) %>">
|
||||
<i class="fa-solid fa-signature me-1"></i><%= attr %>: <%= user.send(attr) %>
|
||||
</span>
|
||||
<% if user.name_for_view.present? %>
|
||||
<span class="badge bg-info text-truncate" title="<%= user.name_for_view %>">
|
||||
<i class="fa-solid fa-signature me-1"></i><%= user.name_for_view %>
|
||||
</span>
|
||||
<% end %>
|
||||
</div>
|
||||
<div class="d-flex align-items-center ms-auto gap-2">
|
||||
<% if user.external_profile_url_for_view.present? %>
|
||||
<%= link_to user.external_profile_url_for_view,
|
||||
class: "badge bg-secondary text-truncate-link",
|
||||
target: "_blank",
|
||||
rel: "noopener noreferrer nofollow" do %>
|
||||
<i class="fa-solid fa-link me-1"></i><%= user.class.view_prefix.upcase %>
|
||||
<% end %>
|
||||
<% end %>
|
||||
<span class="badge <%= user.account_status_for_view == "ok" ? "bg-success" : "bg-danger" %>">
|
||||
<i class="fa-solid <%= user.account_status_for_view == "ok" ? "fa-check" : "fa-exclamation-triangle" %> me-1"></i>
|
||||
<%= user.account_status_for_view %>
|
||||
</span>
|
||||
<% if user.registered_at_for_view.present? %>
|
||||
<span class="badge bg-light text-dark" title="<%= time_ago_in_words(user.registered_at_for_view) %> ago">
|
||||
<i class="fa-regular fa-clock me-1"></i><%= user.registered_at_for_view.strftime("%Y-%m-%d") %>
|
||||
</span>
|
||||
<% end %>
|
||||
<% if user.avatar.present? %>
|
||||
<span class="badge bg-light text-dark">
|
||||
<i class="fa-solid fa-image-portrait me-1"></i>Has Avatar
|
||||
</span>
|
||||
<% end %>
|
||||
</div>
|
||||
</div>
|
||||
@@ -47,16 +47,6 @@ Rails.application.configure do
|
||||
},
|
||||
description: "FurAffinity, scan browse page",
|
||||
},
|
||||
fa_home_page_job: {
|
||||
cron: "*/1 * * * *",
|
||||
class: "Domain::Fa::Job::HomePageJob",
|
||||
args: [{}],
|
||||
set: {
|
||||
queue: "manual",
|
||||
priority: -20,
|
||||
},
|
||||
description: "FurAffinity, scan home page",
|
||||
},
|
||||
e621_posts_index_job: {
|
||||
cron: "*/1 * * * *",
|
||||
class: "Domain::E621::Job::PostsIndexJob",
|
||||
@@ -82,6 +72,8 @@ ActiveSupport.on_load(:good_job_application_controller) do
|
||||
|
||||
class GoodJob::JobsController < GoodJob::ApplicationController
|
||||
helper Domain::PostsHelper
|
||||
helper Domain::UsersHelper
|
||||
helper Domain::PostGroupsHelper
|
||||
helper GoodJobHelper
|
||||
end
|
||||
end
|
||||
|
||||
@@ -38,11 +38,18 @@ Rails.application.routes.draw do
|
||||
constraints: {
|
||||
id: %r{[^/]+/[^/]+},
|
||||
} do
|
||||
resources :users, only: %i[], controller: "domain/usrs", path: "" do
|
||||
resources :users, only: %i[], controller: "domain/users", path: "" do
|
||||
get :faved_by, on: :collection, action: :users_faving_post
|
||||
end
|
||||
end
|
||||
|
||||
resources :pools,
|
||||
only: %i[show],
|
||||
controller: "domain/post_groups",
|
||||
constraints: {
|
||||
id: %r{[^/]+/[^/]+},
|
||||
}
|
||||
|
||||
# Domain::RouteHelper::DOMAIN_POST_RESOURCES.each do |resource_data|
|
||||
# resolve resource_data.klass.name do |post|
|
||||
# domain_post_path(post)
|
||||
|
||||
@@ -176,15 +176,6 @@ namespace :fa do
|
||||
puts "#{Time.now} - browse_page_job - Domain::Fa::Job::BrowsePageJob"
|
||||
end
|
||||
|
||||
desc "run a single home page job"
|
||||
task home_page_job: %i[set_logger_stdout environment] do
|
||||
Domain::Fa::Job::HomePageJob.set(
|
||||
priority: -20,
|
||||
queue: "manual",
|
||||
).perform_later({})
|
||||
puts "#{Time.now} - home_page_job - Domain::Fa::Job::HomePageJob"
|
||||
end
|
||||
|
||||
desc "run a single post scan job"
|
||||
task scan_post_job: %i[set_logger_stdout environment] do |t, args|
|
||||
fa_id = ENV["fa_id"] || raise("must provide fa_id")
|
||||
|
||||
1
sorbet/rbi/dsl/application_controller.rbi
generated
1
sorbet/rbi/dsl/application_controller.rbi
generated
@@ -35,6 +35,7 @@ class ApplicationController
|
||||
include ::HelpersInterface
|
||||
include ::Domain::ModelHelper
|
||||
include ::Domain::PaginationHelper
|
||||
include ::Domain::PostGroupsHelper
|
||||
include ::Domain::PostsHelper
|
||||
include ::Domain::UsersHelper
|
||||
include ::DomainSourceHelper
|
||||
|
||||
1
sorbet/rbi/dsl/devise_controller.rbi
generated
1
sorbet/rbi/dsl/devise_controller.rbi
generated
@@ -32,6 +32,7 @@ class DeviseController
|
||||
include ::HelpersInterface
|
||||
include ::Domain::ModelHelper
|
||||
include ::Domain::PaginationHelper
|
||||
include ::Domain::PostGroupsHelper
|
||||
include ::Domain::PostsHelper
|
||||
include ::Domain::UsersHelper
|
||||
include ::DomainSourceHelper
|
||||
|
||||
84
sorbet/rbi/dsl/domain/post_group/inkbunny_pool.rbi
generated
84
sorbet/rbi/dsl/domain/post_group/inkbunny_pool.rbi
generated
@@ -451,12 +451,21 @@ class Domain::PostGroup::InkbunnyPool
|
||||
sig { params(args: T.untyped, blk: T.untyped).returns(::HttpLogEntry) }
|
||||
def build_deep_update_log_entry(*args, &blk); end
|
||||
|
||||
sig { params(args: T.untyped, blk: T.untyped).returns(::Domain::User::InkbunnyUser) }
|
||||
def build_owner(*args, &blk); end
|
||||
|
||||
sig { params(args: T.untyped, blk: T.untyped).returns(::HttpLogEntry) }
|
||||
def create_deep_update_log_entry(*args, &blk); end
|
||||
|
||||
sig { params(args: T.untyped, blk: T.untyped).returns(::HttpLogEntry) }
|
||||
def create_deep_update_log_entry!(*args, &blk); end
|
||||
|
||||
sig { params(args: T.untyped, blk: T.untyped).returns(::Domain::User::InkbunnyUser) }
|
||||
def create_owner(*args, &blk); end
|
||||
|
||||
sig { params(args: T.untyped, blk: T.untyped).returns(::Domain::User::InkbunnyUser) }
|
||||
def create_owner!(*args, &blk); end
|
||||
|
||||
sig { returns(T.nilable(::HttpLogEntry)) }
|
||||
def deep_update_log_entry; end
|
||||
|
||||
@@ -469,6 +478,18 @@ class Domain::PostGroup::InkbunnyPool
|
||||
sig { returns(T::Boolean) }
|
||||
def deep_update_log_entry_previously_changed?; end
|
||||
|
||||
sig { returns(T.nilable(::Domain::User::InkbunnyUser)) }
|
||||
def owner; end
|
||||
|
||||
sig { params(value: T.nilable(::Domain::User::InkbunnyUser)).void }
|
||||
def owner=(value); end
|
||||
|
||||
sig { returns(T::Boolean) }
|
||||
def owner_changed?; end
|
||||
|
||||
sig { returns(T::Boolean) }
|
||||
def owner_previously_changed?; end
|
||||
|
||||
sig { returns(T::Array[T.untyped]) }
|
||||
def post_group_join_ids; end
|
||||
|
||||
@@ -500,8 +521,14 @@ class Domain::PostGroup::InkbunnyPool
|
||||
sig { returns(T.nilable(::HttpLogEntry)) }
|
||||
def reload_deep_update_log_entry; end
|
||||
|
||||
sig { returns(T.nilable(::Domain::User::InkbunnyUser)) }
|
||||
def reload_owner; end
|
||||
|
||||
sig { void }
|
||||
def reset_deep_update_log_entry; end
|
||||
|
||||
sig { void }
|
||||
def reset_owner; end
|
||||
end
|
||||
|
||||
module GeneratedAssociationRelationMethods
|
||||
@@ -1083,6 +1110,51 @@ class Domain::PostGroup::InkbunnyPool
|
||||
sig { void }
|
||||
def name_will_change!; end
|
||||
|
||||
sig { returns(T.nilable(::Integer)) }
|
||||
def owner_id; end
|
||||
|
||||
sig { params(value: T.nilable(::Integer)).returns(T.nilable(::Integer)) }
|
||||
def owner_id=(value); end
|
||||
|
||||
sig { returns(T::Boolean) }
|
||||
def owner_id?; end
|
||||
|
||||
sig { returns(T.nilable(::Integer)) }
|
||||
def owner_id_before_last_save; end
|
||||
|
||||
sig { returns(T.untyped) }
|
||||
def owner_id_before_type_cast; end
|
||||
|
||||
sig { returns(T::Boolean) }
|
||||
def owner_id_came_from_user?; end
|
||||
|
||||
sig { returns(T.nilable([T.nilable(::Integer), T.nilable(::Integer)])) }
|
||||
def owner_id_change; end
|
||||
|
||||
sig { returns(T.nilable([T.nilable(::Integer), T.nilable(::Integer)])) }
|
||||
def owner_id_change_to_be_saved; end
|
||||
|
||||
sig { params(from: T.nilable(::Integer), to: T.nilable(::Integer)).returns(T::Boolean) }
|
||||
def owner_id_changed?(from: T.unsafe(nil), to: T.unsafe(nil)); end
|
||||
|
||||
sig { returns(T.nilable(::Integer)) }
|
||||
def owner_id_in_database; end
|
||||
|
||||
sig { returns(T.nilable([T.nilable(::Integer), T.nilable(::Integer)])) }
|
||||
def owner_id_previous_change; end
|
||||
|
||||
sig { params(from: T.nilable(::Integer), to: T.nilable(::Integer)).returns(T::Boolean) }
|
||||
def owner_id_previously_changed?(from: T.unsafe(nil), to: T.unsafe(nil)); end
|
||||
|
||||
sig { returns(T.nilable(::Integer)) }
|
||||
def owner_id_previously_was; end
|
||||
|
||||
sig { returns(T.nilable(::Integer)) }
|
||||
def owner_id_was; end
|
||||
|
||||
sig { void }
|
||||
def owner_id_will_change!; end
|
||||
|
||||
sig { void }
|
||||
def restore_count!; end
|
||||
|
||||
@@ -1110,6 +1182,9 @@ class Domain::PostGroup::InkbunnyPool
|
||||
sig { void }
|
||||
def restore_name!; end
|
||||
|
||||
sig { void }
|
||||
def restore_owner_id!; end
|
||||
|
||||
sig { void }
|
||||
def restore_type!; end
|
||||
|
||||
@@ -1170,6 +1245,12 @@ class Domain::PostGroup::InkbunnyPool
|
||||
sig { returns(T::Boolean) }
|
||||
def saved_change_to_name?; end
|
||||
|
||||
sig { returns(T.nilable([T.nilable(::Integer), T.nilable(::Integer)])) }
|
||||
def saved_change_to_owner_id; end
|
||||
|
||||
sig { returns(T::Boolean) }
|
||||
def saved_change_to_owner_id?; end
|
||||
|
||||
sig { returns(T.nilable([T.untyped, T.untyped])) }
|
||||
def saved_change_to_type; end
|
||||
|
||||
@@ -1309,6 +1390,9 @@ class Domain::PostGroup::InkbunnyPool
|
||||
sig { returns(T::Boolean) }
|
||||
def will_save_change_to_name?; end
|
||||
|
||||
sig { returns(T::Boolean) }
|
||||
def will_save_change_to_owner_id?; end
|
||||
|
||||
sig { returns(T::Boolean) }
|
||||
def will_save_change_to_type?; end
|
||||
|
||||
|
||||
3
sorbet/rbi/dsl/generated_path_helpers_module.rbi
generated
3
sorbet/rbi/dsl/generated_path_helpers_module.rbi
generated
@@ -132,6 +132,9 @@ module GeneratedPathHelpersModule
|
||||
sig { params(args: T.untyped).returns(String) }
|
||||
def pg_hero_path(*args); end
|
||||
|
||||
sig { params(args: T.untyped).returns(String) }
|
||||
def pool_path(*args); end
|
||||
|
||||
sig { params(args: T.untyped).returns(String) }
|
||||
def post_path(*args); end
|
||||
|
||||
|
||||
3
sorbet/rbi/dsl/generated_url_helpers_module.rbi
generated
3
sorbet/rbi/dsl/generated_url_helpers_module.rbi
generated
@@ -132,6 +132,9 @@ module GeneratedUrlHelpersModule
|
||||
sig { params(args: T.untyped).returns(String) }
|
||||
def pg_hero_url(*args); end
|
||||
|
||||
sig { params(args: T.untyped).returns(String) }
|
||||
def pool_url(*args); end
|
||||
|
||||
sig { params(args: T.untyped).returns(String) }
|
||||
def post_url(*args); end
|
||||
|
||||
|
||||
1
sorbet/rbi/dsl/rails/application_controller.rbi
generated
1
sorbet/rbi/dsl/rails/application_controller.rbi
generated
@@ -35,6 +35,7 @@ class Rails::ApplicationController
|
||||
include ::HelpersInterface
|
||||
include ::Domain::ModelHelper
|
||||
include ::Domain::PaginationHelper
|
||||
include ::Domain::PostGroupsHelper
|
||||
include ::Domain::PostsHelper
|
||||
include ::Domain::UsersHelper
|
||||
include ::DomainSourceHelper
|
||||
|
||||
@@ -35,6 +35,7 @@ class Rails::Conductor::BaseController
|
||||
include ::HelpersInterface
|
||||
include ::Domain::ModelHelper
|
||||
include ::Domain::PaginationHelper
|
||||
include ::Domain::PostGroupsHelper
|
||||
include ::Domain::PostsHelper
|
||||
include ::Domain::UsersHelper
|
||||
include ::DomainSourceHelper
|
||||
|
||||
1
sorbet/rbi/dsl/rails/health_controller.rbi
generated
1
sorbet/rbi/dsl/rails/health_controller.rbi
generated
@@ -35,6 +35,7 @@ class Rails::HealthController
|
||||
include ::HelpersInterface
|
||||
include ::Domain::ModelHelper
|
||||
include ::Domain::PaginationHelper
|
||||
include ::Domain::PostGroupsHelper
|
||||
include ::Domain::PostsHelper
|
||||
include ::Domain::UsersHelper
|
||||
include ::DomainSourceHelper
|
||||
|
||||
20
sorbet/rbi/shims/active_job.rbi
Normal file
20
sorbet/rbi/shims/active_job.rbi
Normal file
@@ -0,0 +1,20 @@
|
||||
# typed: strict
|
||||
class ActiveJob::Base
|
||||
sig do
|
||||
params(options: T::Hash[Symbol, T.untyped]).returns(
|
||||
ActiveJob::ConfiguredJob,
|
||||
)
|
||||
end
|
||||
def self.set(options)
|
||||
end
|
||||
end
|
||||
|
||||
class ActiveJob::ConfiguredJob
|
||||
sig do
|
||||
params(args: T.untyped, kwargs: T.untyped, block: T.untyped).returns(
|
||||
T.any(ActiveJob::Base, FalseClass),
|
||||
)
|
||||
end
|
||||
def perform_later(*args, **kwargs, &block)
|
||||
end
|
||||
end
|
||||
@@ -229,6 +229,13 @@ describe Domain::Inkbunny::Job::UpdatePostsJob do
|
||||
expect(post_3104200.pools).to be_empty
|
||||
end
|
||||
|
||||
it "sets the owner of the pool to the creator of the post" do
|
||||
perform_now({ ib_post_ids: ib_post_ids })
|
||||
phantom_touch_pool =
|
||||
Domain::PostGroup::InkbunnyPool.find_by(ib_id: 83_746)
|
||||
expect(phantom_touch_pool.owner).to eq(user_thendyart)
|
||||
end
|
||||
|
||||
it "sets correct left/right post IDs in pool joins" do
|
||||
perform_now({ ib_post_ids: ib_post_ids, caused_by_entry: nil })
|
||||
|
||||
|
||||
Reference in New Issue
Block a user