source link url suffix matching
This commit is contained in:
@@ -366,11 +366,21 @@ module Domain::PostsHelper
|
||||
|
||||
const :hosts, T::Array[String]
|
||||
const :patterns, T::Array[Regexp]
|
||||
const :find_proc, T.proc.params(id: String).returns(T.nilable(SourceResult))
|
||||
const :find_proc,
|
||||
T
|
||||
.proc
|
||||
.params(match: MatchData, url: String)
|
||||
.returns(T.nilable(SourceResult))
|
||||
end
|
||||
|
||||
FA_HOSTS = %w[www.furaffinity.net furaffinity.net]
|
||||
IB_HOSTS = %w[www.inkbunny.net inkbunny.net]
|
||||
FA_HOSTS = %w[*.furaffinity.net furaffinity.net]
|
||||
FA_CDN_HOSTS = %w[d.furaffinity.net *.facdn.net facdn.net]
|
||||
IB_HOSTS = %w[*.inkbunny.net inkbunny.net]
|
||||
IB_CDN_HOSTS = %w[*.ib.metapix.net ib.metapix.net]
|
||||
|
||||
URL_SUFFIX_QUERY = T.let(<<-SQL.strip.chomp.freeze, String)
|
||||
reverse(lower(json_attributes->>'url_str')) LIKE (reverse(lower(?)) || '%')
|
||||
SQL
|
||||
|
||||
MATCHERS =
|
||||
T.let(
|
||||
@@ -379,8 +389,27 @@ module Domain::PostsHelper
|
||||
SourceMatcher.new(
|
||||
hosts: FA_HOSTS,
|
||||
patterns: [%r{/view/(\d+)/?}],
|
||||
find_proc: ->(id) do
|
||||
if post = Domain::Post::FaPost.find_by(fa_id: id)
|
||||
find_proc: ->(match, _) do
|
||||
if post = Domain::Post::FaPost.find_by(fa_id: match[1])
|
||||
SourceResult.new(model: post, title: post.title_for_view)
|
||||
end
|
||||
end,
|
||||
),
|
||||
# Furaffinity posts via direct file URL
|
||||
SourceMatcher.new(
|
||||
hosts: FA_CDN_HOSTS,
|
||||
patterns: [//],
|
||||
find_proc: ->(_, url) do
|
||||
url = Addressable::URI.parse(url)
|
||||
|
||||
post_file =
|
||||
Domain::PostFile.where(
|
||||
"#{URL_SUFFIX_QUERY} OR #{URL_SUFFIX_QUERY}",
|
||||
"d.furaffinity.net#{url.path}",
|
||||
"facdn.net#{url.path}",
|
||||
).first
|
||||
|
||||
if post_file && (post = post_file.post)
|
||||
SourceResult.new(model: post, title: post.title_for_view)
|
||||
end
|
||||
end,
|
||||
@@ -389,8 +418,8 @@ module Domain::PostsHelper
|
||||
SourceMatcher.new(
|
||||
hosts: FA_HOSTS,
|
||||
patterns: [%r{/user/([^/]+)/?}],
|
||||
find_proc: ->(url_name) do
|
||||
if user = Domain::User::FaUser.find_by(url_name: url_name)
|
||||
find_proc: ->(match, _) do
|
||||
if user = Domain::User::FaUser.find_by(url_name: match[1])
|
||||
SourceResult.new(
|
||||
model: user,
|
||||
title: user.name_for_view || "unknown",
|
||||
@@ -402,21 +431,38 @@ module Domain::PostsHelper
|
||||
SourceMatcher.new(
|
||||
hosts: IB_HOSTS,
|
||||
patterns: [%r{/s/(\d+)/?}, %r{/submissionview\.php\?id=(\d+)/?}],
|
||||
find_proc: ->(id) do
|
||||
if post = Domain::Post::InkbunnyPost.find_by(ib_id: id)
|
||||
find_proc: ->(match, _) do
|
||||
if post = Domain::Post::InkbunnyPost.find_by(ib_id: match[1])
|
||||
SourceResult.new(model: post, title: post.title_for_view)
|
||||
end
|
||||
end,
|
||||
),
|
||||
# Inkbunny posts via direct file URL
|
||||
SourceMatcher.new(
|
||||
hosts: IB_CDN_HOSTS,
|
||||
patterns: [//],
|
||||
find_proc: ->(_, url) do
|
||||
url = Addressable::URI.parse(url)
|
||||
if post_file =
|
||||
Domain::PostFile.where(
|
||||
"#{URL_SUFFIX_QUERY}",
|
||||
"ib.metapix.net#{url.path}",
|
||||
).first
|
||||
if post = post_file.post
|
||||
SourceResult.new(model: post, title: post.title_for_view)
|
||||
end
|
||||
end
|
||||
end,
|
||||
),
|
||||
# Inkbunny users
|
||||
SourceMatcher.new(
|
||||
hosts: IB_HOSTS,
|
||||
patterns: [%r{/(\w+)/?$}],
|
||||
find_proc: ->(name) do
|
||||
find_proc: ->(match, _) do
|
||||
if user =
|
||||
Domain::User::InkbunnyUser.where(
|
||||
"lower(json_attributes->>'name') = lower(?)",
|
||||
name,
|
||||
match[1],
|
||||
).first
|
||||
SourceResult.new(
|
||||
model: user,
|
||||
@@ -437,12 +483,16 @@ module Domain::PostsHelper
|
||||
source.downcase!
|
||||
source = "https://" + source unless source.include?("://")
|
||||
uri = URI.parse(source)
|
||||
uri_host = uri.host
|
||||
return nil if uri_host.blank?
|
||||
|
||||
for matcher in MATCHERS
|
||||
if matcher.hosts.include?(uri.host)
|
||||
if matcher.hosts.any? { |host|
|
||||
File.fnmatch?(host, uri_host, File::FNM_PATHNAME)
|
||||
}
|
||||
for pattern in matcher.patterns
|
||||
if (match = pattern.match(uri.to_s)) && (id = match[1])
|
||||
object = matcher.find_proc.call(id)
|
||||
if (match = pattern.match(uri.to_s))
|
||||
object = matcher.find_proc.call(match, uri.to_s)
|
||||
return nil unless object
|
||||
model = object.model
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<% source = "https://#{source}" %>
|
||||
<% end %>
|
||||
<div class="flex items-center justify-between gap-2 py-1 first:pt-0 last:pb-0">
|
||||
<div class="flex items-center gap-2 justify-center h-full max-w-full">
|
||||
<div class="flex items-center gap-2 justify-center h-full min-w-0 flex-shrink">
|
||||
<% if icon_path = icon_asset_for_url(source) %>
|
||||
<%= image_tag icon_path, class: "h-6 w-6 mt-1 flex-shrink-0" %>
|
||||
<% else %>
|
||||
@@ -16,7 +16,7 @@
|
||||
<% if lfs = link_for_source(source) %>
|
||||
<%= link_to lfs.model_path,
|
||||
class:
|
||||
"flex items-center rounded overflow-hidden bg-blue-600 text-sm text-white hover:bg-blue-700 h-8" do %>
|
||||
"flex items-center rounded overflow-hidden bg-blue-600 text-sm text-white hover:bg-blue-700 h-8 flex-shrink-0" do %>
|
||||
<% if lfs.icon_path %>
|
||||
<%= image_tag lfs.icon_path, class: "h-full" %>
|
||||
<% end %>
|
||||
|
||||
@@ -4,13 +4,27 @@ class AddUrlIndexToDomainPostFiles < ActiveRecord::Migration[7.2]
|
||||
|
||||
sig { void }
|
||||
def change
|
||||
add_json_index :domain_post_files, :url_str
|
||||
up_only { execute "SET default_tablespace = mirai" }
|
||||
|
||||
# prefix search file URLs
|
||||
add_index :domain_post_files,
|
||||
"lower((json_attributes->>'url_str')::text) text_pattern_ops",
|
||||
name: "idx_domain_post_files_on_url_str_lower"
|
||||
|
||||
# suffix search file URLs
|
||||
add_index :domain_post_files,
|
||||
"reverse(lower((json_attributes->>'url_str')::text)) text_pattern_ops",
|
||||
name: "idx_domain_post_files_on_url_str_lower_reverse"
|
||||
|
||||
# InkbunnyUser#name
|
||||
add_index :domain_users,
|
||||
"lower((json_attributes->>'name')::text)",
|
||||
"lower((json_attributes->>'name')::text) text_pattern_ops",
|
||||
name: "idx_domain_users_inkbunny_on_name_lower",
|
||||
where: "type = 'Domain::User::InkbunnyUser'"
|
||||
|
||||
# E621User#name
|
||||
add_index :domain_users,
|
||||
"lower((json_attributes->>'name')::text)",
|
||||
"lower((json_attributes->>'name')::text) text_pattern_ops",
|
||||
name: "idx_domain_users_e621_on_name_lower",
|
||||
where: "type = 'Domain::User::E621User'"
|
||||
end
|
||||
|
||||
@@ -5560,30 +5560,33 @@ CREATE UNIQUE INDEX idx_domain_inkbunny_users_on_ib_id ON public.domain_users US
|
||||
CREATE UNIQUE INDEX idx_domain_inkbunny_users_on_name ON public.domain_users USING btree (((json_attributes ->> 'name'::text))) WHERE (type = 'Domain::User::InkbunnyUser'::public.domain_user_type);
|
||||
|
||||
|
||||
SET default_tablespace = '';
|
||||
|
||||
--
|
||||
-- Name: idx_domain_post_files_on_url_str; Type: INDEX; Schema: public; Owner: -
|
||||
-- Name: idx_domain_post_files_on_url_str_lower; Type: INDEX; Schema: public; Owner: -; Tablespace: mirai
|
||||
--
|
||||
|
||||
CREATE INDEX idx_domain_post_files_on_url_str ON public.domain_post_files USING btree (((json_attributes ->> 'url_str'::text)));
|
||||
CREATE INDEX idx_domain_post_files_on_url_str_lower ON public.domain_post_files USING btree (lower((json_attributes ->> 'url_str'::text)) text_pattern_ops);
|
||||
|
||||
|
||||
--
|
||||
-- Name: idx_domain_users_e621_on_name_lower; Type: INDEX; Schema: public; Owner: -
|
||||
-- Name: idx_domain_post_files_on_url_str_lower_reverse; Type: INDEX; Schema: public; Owner: -; Tablespace: mirai
|
||||
--
|
||||
|
||||
CREATE INDEX idx_domain_users_e621_on_name_lower ON public.domain_users USING btree (lower((json_attributes ->> 'name'::text))) WHERE (type = 'Domain::User::E621User'::public.domain_user_type);
|
||||
CREATE INDEX idx_domain_post_files_on_url_str_lower_reverse ON public.domain_post_files USING btree (reverse(lower((json_attributes ->> 'url_str'::text))) text_pattern_ops);
|
||||
|
||||
|
||||
--
|
||||
-- Name: idx_domain_users_inkbunny_on_name_lower; Type: INDEX; Schema: public; Owner: -
|
||||
-- Name: idx_domain_users_e621_on_name_lower; Type: INDEX; Schema: public; Owner: -; Tablespace: mirai
|
||||
--
|
||||
|
||||
CREATE INDEX idx_domain_users_inkbunny_on_name_lower ON public.domain_users USING btree (lower((json_attributes ->> 'name'::text))) WHERE (type = 'Domain::User::InkbunnyUser'::public.domain_user_type);
|
||||
CREATE INDEX idx_domain_users_e621_on_name_lower ON public.domain_users USING btree (lower((json_attributes ->> 'name'::text)) text_pattern_ops) WHERE (type = 'Domain::User::E621User'::public.domain_user_type);
|
||||
|
||||
|
||||
SET default_tablespace = mirai;
|
||||
--
|
||||
-- Name: idx_domain_users_inkbunny_on_name_lower; Type: INDEX; Schema: public; Owner: -; Tablespace: mirai
|
||||
--
|
||||
|
||||
CREATE INDEX idx_domain_users_inkbunny_on_name_lower ON public.domain_users USING btree (lower((json_attributes ->> 'name'::text)) text_pattern_ops) WHERE (type = 'Domain::User::InkbunnyUser'::public.domain_user_type);
|
||||
|
||||
|
||||
--
|
||||
-- Name: idx_domain_users_on_migrated_user_favs_at; Type: INDEX; Schema: public; Owner: -; Tablespace: mirai
|
||||
|
||||
@@ -460,6 +460,69 @@ RSpec.describe Domain::PostsHelper, type: :helper do
|
||||
expect(url).to eq_link_for_source(model: user, title: "artistone")
|
||||
end
|
||||
end
|
||||
|
||||
%w[
|
||||
https://ib.metapix.net/s/abc/3210.jpg
|
||||
http://ib.metapix.net/s/abc/3210.jpg
|
||||
http://www.ib.metapix.net/s/abc/3210.jpg
|
||||
ib.metapix.net/s/abc/3210.jpg
|
||||
gb2.ib.metapix.net/s/abc/3210.jpg
|
||||
gb2.ib.metapix.net/s/abc/3210.jpg
|
||||
https://gb2.ib.metapix.net/s/abc/3210.jpg
|
||||
].each do |url|
|
||||
it "works with inkbunny file URL #{url}" do
|
||||
post =
|
||||
create(
|
||||
:domain_post_inkbunny_post,
|
||||
ib_id: "123456",
|
||||
title: "Post Title",
|
||||
)
|
||||
post.files.create!(url_str: url, ib_id: "78910")
|
||||
expect(url).to eq_link_for_source(model: post, title: "Post Title")
|
||||
end
|
||||
end
|
||||
|
||||
%w[
|
||||
https://d.facdn.net/art/dragonkai/1574894326/1574894326.dragonkai_tiefling_warlock_fortune.png
|
||||
https://facdn.net/art/dragonkai/1574894326/1574894326.dragonkai_tiefling_warlock_fortune.png
|
||||
www.facdn.net/art/dragonkai/1574894326/1574894326.dragonkai_tiefling_warlock_fortune.png
|
||||
facdn.net/art/dragonkai/1574894326/1574894326.dragonkai_tiefling_warlock_fortune.png
|
||||
].each do |url|
|
||||
it "works with FA file URL #{url}" do
|
||||
post =
|
||||
create(:domain_post_fa_post, fa_id: "123456", title: "Post Title")
|
||||
post.files.create!(url_str: url)
|
||||
expect(url).to eq_link_for_source(model: post, title: "Post Title")
|
||||
end
|
||||
end
|
||||
|
||||
it "has the right avatar url" do
|
||||
user =
|
||||
create(
|
||||
:domain_user_fa_user,
|
||||
url_name: "artistone",
|
||||
name: "Artist One",
|
||||
)
|
||||
create(:domain_user_avatar, user:)
|
||||
|
||||
link_for_source =
|
||||
helper.link_for_source("https://www.furaffinity.net/user/artistone/")
|
||||
expect(link_for_source).to be_present
|
||||
expect(link_for_source.icon_path).to eq(
|
||||
helper.domain_user_avatar_img_src_path(
|
||||
user.avatar,
|
||||
thumb: "64-avatar",
|
||||
),
|
||||
)
|
||||
end
|
||||
|
||||
it "has the right model path" do
|
||||
create(:domain_post_fa_post, fa_id: "123456", title: "Post Title")
|
||||
link_for_source =
|
||||
helper.link_for_source("https://www.furaffinity.net/view/123456/")
|
||||
expect(link_for_source).to be_present
|
||||
expect(link_for_source.model_path).to eq("/posts/fa/123456")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
Reference in New Issue
Block a user