Merge branch 'master' into missing-br

This commit is contained in:
Kyle Drake 2023-07-19 16:32:15 -05:00 committed by GitHub
commit 22b8af52c3
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
20 changed files with 311 additions and 136 deletions

View file

@ -8,7 +8,7 @@ gem 'bcrypt'
gem 'sinatra-flash', require: 'sinatra/flash'
gem 'sinatra-xsendfile', require: 'sinatra/xsendfile'
gem 'puma', '5.6.5', require: nil
gem 'sidekiq', '~> 5.2.0'
gem 'sidekiq', '~> 7.0.8'
gem 'mail'
gem 'net-smtp'
gem 'tilt'

View file

@ -49,7 +49,7 @@ GEM
climate_control (0.2.0)
coderay (1.1.3)
concurrent-ruby (1.2.2)
connection_pool (2.3.0)
connection_pool (2.4.0)
coveralls_reborn (0.25.0)
simplecov (>= 0.18.1, < 0.22.0)
term-ansicolor (~> 1.6)
@ -187,11 +187,11 @@ GEM
net-protocol
netrc (0.11.0)
nio4r (2.5.8)
nokogiri (1.13.10-x86_64-linux)
nokogiri (1.14.3-x86_64-linux)
racc (~> 1.4)
ox (2.14.11)
paypal-recurring (1.1.0)
pg (1.4.4)
pg (1.5.3)
progress (3.6.0)
pry (0.14.1)
coderay (~> 1.1)
@ -199,7 +199,7 @@ GEM
public_suffix (5.0.0)
puma (5.6.5)
nio4r (~> 2.0)
racc (1.6.2)
racc (1.7.1)
rack (2.2.6.4)
rack-cache (1.13.0)
rack (>= 0.4)
@ -215,6 +215,8 @@ GEM
rb-inotify (0.10.1)
ffi (~> 1.0)
redis (4.5.1)
redis-client (0.14.1)
connection_pool
redis-namespace (1.9.0)
redis (>= 4)
regexp_parser (2.6.0)
@ -230,7 +232,7 @@ GEM
rszr (1.3.0)
ruby-progressbar (1.11.0)
ruby2_keywords (0.0.5)
sanitize (6.0.1)
sanitize (6.0.2)
crass (~> 1.0.2)
nokogiri (>= 1.12.0)
sass (3.7.4)
@ -239,17 +241,17 @@ GEM
rb-fsevent (~> 0.9, >= 0.9.4)
rb-inotify (~> 0.9, >= 0.9.7)
sax-machine (1.3.2)
sequel (5.62.0)
sequel_pg (1.17.0)
sequel (5.68.0)
sequel_pg (1.17.1)
pg (>= 0.18.0, != 1.2.0)
sequel (>= 4.38.0)
shotgun (0.9.2)
rack (>= 1.0)
sidekiq (5.2.10)
connection_pool (~> 2.2, >= 2.2.2)
rack (~> 2.0)
rack-protection (>= 1.5.0)
redis (~> 4.5, < 4.6.0)
sidekiq (7.0.8)
concurrent-ruby (< 2)
connection_pool (>= 2.3.0)
rack (>= 2.2.4)
redis-client (>= 0.11.0)
simplecov (0.21.2)
docile (~> 1.1)
simplecov-html (~> 0.11)
@ -370,7 +372,7 @@ DEPENDENCIES
sequel
sequel_pg
shotgun
sidekiq (~> 5.2.0)
sidekiq (~> 7.0.8)
simplecov
simpleidn
sinatra

View file

@ -1,7 +1,12 @@
CREATE_MATCH_REGEX = /^username$|^password$|^email$|^new_tags_string$|^is_education$/
def education_whitelist_required?
return true if params[:is_education] == 'true' && $config['education_tag_whitelist']
false
end
def education_whitelisted?
return true if params[:is_education] == 'true' && $config['education_tag_whitelist'] && !$config['education_tag_whitelist'].select {|t| params[:new_tags_string].match(t)}.empty?
return true if education_whitelist_required? && !$config['education_tag_whitelist'].select {|t| params[:new_tags_string].match(t)}.empty?
false
end
@ -63,8 +68,13 @@ post '/create' do
ga_adgroupid: session[:ga_adgroupid]
)
if education_whitelisted?
@site.email_confirmed = true
if education_whitelist_required?
if education_whitelisted?
@site.email_confirmed = true
else
flash[:error] = 'The class tag is invalid.'
return {result: 'error'}.to_json
end
else
if !hcaptcha_valid?
flash[:error] = 'The captcha was not valid, please try again.'

View file

@ -20,6 +20,9 @@ get '/settings/:username/?' do |username|
pass if Site.select(:id).where(username: username).first.nil?
require_login
require_ownership_for_settings
@bluesky_did = $redis_proxy.hget "dns-_atproto.#{@site.username}.neocities.org", 'TXT'
@title = "Site settings for #{username}"
erb :'settings/site'
end
@ -174,15 +177,33 @@ post '/settings/:username/custom_domain' do
end
end
post '/settings/:username/bluesky_set_did' do
require_login
require_ownership_for_settings
redirect '/settings' if !@site.domain.empty?
# todo standards based validation
if params[:did].length > 50
flash[:error] = 'DID provided was too long'
elsif !params[:did].match(/^did=did:plc:([a-z|0-9)]+)$/)
flash[:error] = 'DID was invalid'
else
$redis_proxy.hset "dns-_atproto.#{@site.username}.neocities.org", 'TXT', params[:did]
flash[:success] = 'DID set! You can now verify the domain on the Bluesky app.'
end
redirect "/settings/#{@site.username}#bluesky"
end
post '/settings/:username/generate_api_key' do
require_login
require_ownership_for_settings
is_new = current_site.api_key.nil?
current_site.generate_api_key!
is_new = @site.api_key.nil?
@site.generate_api_key!
msg = is_new ? "New API key has been generated." : "API key has been regenerated."
flash[:success] = msg
redirect "/settings/#{current_site.username}#api_key"
redirect "/settings/#{@site.username}#api_key"
end
post '/settings/change_password' do

View file

@ -35,6 +35,7 @@ class Event < Sequel::Model
ds = select_all(:events).
order(:created_at.desc).
join_table(:inner, :sites, id: :site_id).
where(follow_id: nil).
exclude(Sequel.qualify(:sites, :is_deleted) => true).
exclude(Sequel.qualify(:events, :is_deleted) => true).
exclude(is_banned: true)

View file

@ -35,6 +35,7 @@ class Site < Sequel::Model
application/rss+xml
application/x-elc
image/webp
image/avif
image/x-xcf
application/epub
application/epub+zip
@ -43,11 +44,11 @@ class Site < Sequel::Model
}
VALID_EXTENSIONS = %w{
html htm txt text css js jpg jpeg png gif svg md markdown eot ttf woff woff2 json geojson csv tsv mf ico pdf asc key pgp xml mid midi manifest otf webapp less sass rss kml dae obj mtl scss webp xcf epub gltf bin webmanifest knowl atom opml rdf map gpg
html htm txt text css js jpg jpeg png gif svg md markdown eot ttf woff woff2 json geojson csv tsv mf ico pdf asc key pgp xml mid midi manifest otf webapp less sass rss kml dae obj mtl scss webp avif xcf epub gltf bin webmanifest knowl atom opml rdf map gpg resolveHandle
}
VALID_EDITABLE_EXTENSIONS = %w{
html htm txt js css scss md manifest less webmanifest xml json opml rdf svg gpg pgp
html htm txt js css scss md manifest less webmanifest xml json opml rdf svg gpg pgp resolveHandle
}
MINIMUM_PASSWORD_LENGTH = 5
@ -166,6 +167,7 @@ class Site < Sequel::Model
MAX_COMMENTS_PER_DAY = 5
SANDBOX_TIME = 14.days
BLACK_BOX_WAIT_TIME = 10.seconds
MAX_DISPLAY_FOLLOWS = 56*3
many_to_many :tags
@ -568,8 +570,8 @@ class Site < Sequel::Model
follows_dataset.all
end
def profile_follows_actioning_ids
follows_dataset.select(:actioning_site_id).exclude(:sites__site_changed => false).all
def profile_follows_actioning_ids(limit=nil)
follows_dataset.select(:actioning_site_id).exclude(:sites__site_changed => false).limit(limit).all
end
=begin
@ -1447,6 +1449,10 @@ class Site < Sequel::Model
paginate(current_page, limit)
end
def newest_follows
follows_dataset.where(:follows__created_at => (1.month.ago..Time.now)).order(:follows__created_at.desc).all
end
def host
!domain.empty? ? domain : "#{username}.neocities.org"
end

View file

@ -145,6 +145,35 @@ describe 'site/settings' do
end
end
describe 'api key' do
include Capybara::DSL
before do
Capybara.reset_sessions!
@site = Fabricate :site
@child_site = Fabricate :site, parent_site_id: @site.id
page.set_rack_session id: @site.id
end
it 'sets api key' do
visit "/settings/#{@child_site[:username]}#api_key"
_(@site.api_key).must_be_nil
_(@child_site.api_key).must_be_nil
click_button 'Generate API Key'
_(@site.reload.api_key).must_be_nil
_(@child_site.reload.api_key).wont_be_nil
_(page.body).must_match @child_site.api_key
end
it 'regenerates api key for child site' do
visit "/settings/#{@child_site[:username]}#api_key"
@child_site.generate_api_key!
api_key = @child_site.api_key
click_button 'Generate API Key'
_(@child_site.reload.api_key).wont_equal api_key
end
end
describe 'delete' do
include Capybara::DSL

View file

@ -64,36 +64,45 @@ describe 'site page' do
end
it 'allows site blocking and unblocking' do
tag = SecureRandom.hex 10
blocked_site = Fabricate :site, new_tags_string: tag, created_at: 2.weeks.ago, site_changed: true, views: Site::BROWSE_MINIMUM_FOLLOWER_VIEWS+1
site = Fabricate :site
describe 'blocking' do
before do
@tag = SecureRandom.hex 10
@blocked_site = Fabricate :site, new_tags_string: @tag, created_at: 2.weeks.ago, site_changed: true, views: Site::BROWSE_MINIMUM_FOLLOWER_VIEWS+1
end
page.set_rack_session id: site.id
after do
@blocked_site.destroy
end
visit "/browse?tag=#{tag}"
it 'allows site blocking and unblocking' do
site = Fabricate :site
_(page.find('.website-Gallery .username a')['href']).must_match /\/site\/#{blocked_site.username}/
page.set_rack_session id: site.id
visit "/site/#{blocked_site.username}"
visit "/browse?tag=#{@tag}"
click_link 'Block'
click_button 'Block Site'
_(page.find('.website-Gallery .username a')['href']).must_match /\/site\/#{@blocked_site.username}/
visit "/browse?tag=#{tag}"
visit "/site/#{@blocked_site.username}"
_(page).must_have_content /no active sites found/i
click_link 'Block'
click_button 'Block Site'
site.reload
_(site.blockings.length).must_equal 1
_(site.blockings.first.site_id).must_equal blocked_site.id
visit "/browse?tag=#{@tag}"
visit "/site/#{blocked_site.username}"
_(page).must_have_content /no active sites found/i
click_link 'Unblock'
site.reload
_(site.blockings.length).must_equal 1
_(site.blockings.first.site_id).must_equal @blocked_site.id
visit "/browse?tag=#{tag}"
_(page.find('.website-Gallery .username a')['href']).must_match /\/site\/#{blocked_site.username}/
visit "/site/#{@blocked_site.username}"
click_link 'Unblock'
visit "/browse?tag=#{@tag}"
_(page.find('.website-Gallery .username a')['href']).must_match /\/site\/#{@blocked_site.username}/
end
end
it '404s if site is banned' do

View file

@ -4,7 +4,28 @@ DEBIAN_FRONTEND=noninteractive
. /vagrant/vagrant/redis.sh
apt-get install -y git curl zlib1g-dev build-essential libssl-dev libreadline-dev libyaml-dev libsqlite3-dev sqlite3 libxml2-dev libxslt1-dev libcurl4-openssl-dev libffi-dev libpq-dev libmagickwand-dev imagemagick libmagickwand-dev libmagic-dev file clamav-daemon
apt-get install -y \
build-essential \
clamav-daemon \
curl \
file \
git \
imagemagick \
libcurl4-openssl-dev \
libffi-dev \
libimlib2-dev \
libmagic-dev \
libmagickwand-dev \
libpq-dev \
libreadline-dev \
libsqlite3-dev \
libssl-dev \
libwebp-dev \
libxml2-dev \
libxslt1-dev \
libyaml-dev \
sqlite3 \
zlib1g-dev
sed -i 's|[#]*DetectPUA false|DetectPUA true|g' /etc/clamav/clamd.conf

View file

@ -1,8 +1,8 @@
<% site_followings = site.followings %>
<% if (!is_current_site && !site_followings.empty?) || is_current_site %>
<% site_followings = site.followings_dataset.count %>
<% if (!is_current_site && site_followings > 0) || is_current_site %>
<div class="following-list">
<h3><a href="/site/<%= site.username %>/follows"><%= is_current_site ? 'Sites you follow' : 'This site follows' %></a></h3>
<% if site_followings.empty? %>
<% if site_followings == 0 %>
<p>You are not following any sites yet. Add some by <a href="/browse">browsing sites</a> or looking at your tags.
<% else %>
<% site.followings_dataset.select(:site_id).all.each do |following| %>
@ -12,17 +12,22 @@
</div>
<% end %>
<% site_follows = site.follows %>
<% if (!is_current_site && !site_follows.empty?) || is_current_site %>
<% site_follows = site.follows_dataset.count %>
<% if (!is_current_site && site_follows > 0) || is_current_site %>
<div class="follower-list">
<h3><a href="/site/<%= site.username %>/followers">Followers</a></h3>
<% if site_follows.empty? %>
<% if site_follows == 0 %>
No followers yet.
<% else %>
<% site.profile_follows_actioning_ids.each do |follow| %>
<% site_profile_follows_actioning_ids = site.profile_follows_actioning_ids %>
<% site_profile_follows_actioning_ids[0...Site::MAX_DISPLAY_FOLLOWS].each do |follow| %>
<% follow_actioning_site = follow.actioning_site_dataset.select(:username).first %>
<a href="/site/<%= follow_actioning_site.username %>" title="<%= follow_actioning_site.title %>"><img src="<%= follow_actioning_site.screenshot_url 'index.html', '50x50' %>" class="avatar" onerror="this.src='/img/50x50.png'"></a>
<% end %>
<% if Site::MAX_DISPLAY_FOLLOWS < site_profile_follows_actioning_ids.count %>
<a href="/site/<%= site.username %>/followers"><strong>see more <i class="fa fa-arrow-right"></i></strong></a>
<% end %>
<% end %>
</div>
<% end %>

View file

@ -10,51 +10,32 @@
<script src="/js/news/event.js"></script>
<script src="/js/news/site.js"></script>
<% if defined?(site) && !params[:event_id] %>
<% follow_events = site.newest_follows %>
<% unless follow_events.empty? %>
<div class="news-item follow">
<div class="icon"><a href="/site/<%= site.username %>" title="<%= site.username %>" class="avatar" style="background-image: url(<%= site.screenshot_url 'index.html', '50x50' %>);"></a></div>
<div class="text">
<strong>New Followers</strong>
</div>
<div class="content">
<% follow_events.first.site.newest_follows.each_with_index do |event,i| %>
<a href="/site/<%= event.actioning_site.username %>" class="user" title="<%= event.actioning_site.title %>"><i class="fa fa-user"><% if event.actioning_site.supporter? %><i class="fa fa-heart"></i><% end %></i><%= event.actioning_site.username %></a><% unless follow_events.length == i+1 %>, <% end %>
<% end %>
</div>
</div>
<% end %>
<% end %>
<% events.each do |event| %>
<% if event.profile_comment_id %>
<div class="news-item comment" id="event_<%= event.id %>">
<%== erb :'_news_profile_comment', layout: false, locals: {profile_comment: event.profile_comment, event: event} %>
<% elsif event.tip_id %>
<% actioning_site = event.actioning_site_dataset.select(:id, :username, :title, :domain, :stripe_customer_id, :plan_type).first %>
<% event_site = event.site_dataset.select(:id, :username, :title, :domain, :stripe_customer_id, :plan_type).first %>
<% tip = event.tip %>
<div class="news-item tip">
<div class="title">
<div class="icon">
<% if actioning_site %>
<a href="/site/<%= actioning_site.username %>" title="<%= actioning_site.username %>" class="avatar" style="background-image: url(<%= actioning_site.screenshot_url 'index.html', '50x50' %>);"></a>
<% end %>
</div>
<div class="text">
<div class="headline">
<% if actioning_site %>
<% if current_site && current_site.id == actioning_site.id %>
<a href="/site/<%= current_site.username %>" class="you">You</a>
<% else %>
<a href="/site/<%= actioning_site.username %>" class="user" title="<%= actioning_site.title %>"><i class="fa fa-user"><% if actioning_site.supporter? %><i class="fa fa-heart"></i><% end %></i><%= actioning_site.username %></a>
<% end %>
<% else %>
An anonymous donor
<% end %>
sent a <strong><%= tip.amount_string %></strong> tip to
<% if current_site && event_site.id == current_site.id %>
<a href="/site/<%= current_site.username %>" class="you">you</a>!
<% else %>
<a href="/site/<%= event_site.username %>" class="user" title="<%= event_site.title %>"><i class="fa fa-user"><% if event_site.supporter? %><i class="fa fa-heart"></i><% end %></i><%= event_site.username %></a>!
<% end %>
</div>
<span class="comment"><%= tip.message %></span>
</div>
<span class="date">
<a href="/site/<%= event_site.username %>?event_id=<%= event.id %>"><%= event.created_at.ago %></a>
</span>
</div>
<div class="news-item comment" id="event_<%= event.id %>">
<%== erb :'_news_tip', layout: false, locals: {tip: event.tip, event: event} %>
<% elsif event.follow_id %>
<% actioning_site = event.actioning_site_dataset.select(:id, :username, :title, :domain, :stripe_customer_id, :plan_type).first %>
<% next if actioning_site.nil? %>

36
views/_news_tip.erb Normal file
View file

@ -0,0 +1,36 @@
<% actioning_site = event.tip.actioning_site_dataset.select(:id, :username, :stripe_customer_id, :plan_type, :parent_site_id).first %>
<% event_site = event.site_dataset.select(:id, :username, :stripe_customer_id, :plan_type, :parent_site_id).first %>
<div class="title">
<div class="icon">
<% if actioning_site %>
<a href="/site/<%= actioning_site.username %>" title="<%= actioning_site.username %>" class="avatar" style="background-image: url(<%= actioning_site.screenshot_url 'index.html', '50x50' %>);"></a>
<% end %>
</div>
<div class="text">
<% if actioning_site %>
<% if current_site && current_site.id == actioning_site.id %>
<a href="/site/<%= current_site.username %>" class="you">You</a>
<% else %>
<a href="/site/<%= actioning_site.username %>" class="user" title="<%= actioning_site.title %>"><i class="fa fa-user"><% if actioning_site.supporter? %><i class="fa fa-heart"></i><% end %></i><%= actioning_site.username %></a>
<% end %>
<% else %>
An anonymous donor
<% end %>
sent a <strong style="color: #229954 !important;"><%= tip.amount_string %></strong> tip to
<% if current_site && event_site.id == current_site.id %>
<a href="/site/<%= current_site.username %>" class="you">you</a>
<% else %>
<a href="/site/<%= event_site.username %>" class="user" title="<%= event_site.title %>"><i class="fa fa-user"><% if event_site.supporter? %><i class="fa fa-heart"></i><% end %></i><%= event_site.username %></a>
<% end %>
</div>
<span class="date">
<a href="/site/<%= event_site.username %>?event_id=<%= event.id %>"><%= event.created_at.ago %></a>
</span>
</div>
<div class="content"><%== sanitize_comment tip.message %></div>

View file

@ -13,4 +13,4 @@
<br>
<a href="https://www.tumblr.com/share?<%= Rack::Utils.build_query(v: 3, u: "#{page_uri}", t: "#{site.title}") %>" target="_blank">Tumblr</a>
<br>
<a href="https://toot.kytta.dev/?<%= Rack::Utils.build_query(text: "#{page_uri}") %>" target="_blank">Mastodon</a>
<a href="https://toot.kytta.dev/?<%= Rack::Utils.build_query(text: "#{site.title}: #{page_uri}") %>" target="_blank">Mastodon</a>

View file

@ -26,15 +26,19 @@
<li class="active"><a href="#profile" data-toggle="tab">Profile</a></li>
<!-- <li><a href="#domain" data-toggle="tab">Domain Name</a></li> -->
<li><a href="#custom_domain" data-toggle="tab">Custom Domain</a></li>
<li><a href="#username" data-toggle="tab">Change Site Name</a></li>
<li><a href="#username" data-toggle="tab">Rename</a></li>
<li><a href="#tipping" data-toggle="tab">Tipping</a></li>
<li><a href="#api_key" data-toggle="tab">API Key</a></li>
<li><a href="#api_key" data-toggle="tab">API</a></li>
<% if @site.admin_nsfw != true %>
<li><a href="#nsfw" data-toggle="tab">18+</a></li>
<% end %>
<% if @site.domain.empty? %>
<li><a href="#bluesky" data-toggle="tab">Bluesky</a></li>
<% end %>
<li><a href="#delete" data-toggle="tab">Delete</a></li>
</ul>
<div class="tab-content">
@ -64,6 +68,10 @@
</div>
<% end %>
<div class="tab-pane" id="bluesky">
<%== erb :'settings/site/bluesky' %>
</div>
<div class="tab-pane" id="delete">
<%== erb :'settings/site/delete' %>
</div>

View file

@ -0,0 +1,24 @@
<h2>Bluesky Integration (beta)</h2>
<p>
You can now verify control of your site on Neocities to create a handle on <a href="https://bsky.app/">Bluesky</a>.
</p>
<p>
<strong>Bluesky App <i class="fa fa-arrow-right"></i> "Settings" <i class="fa fa-arrow-right"></i> "Change my handle" <i class="fa fa-arrow-right"></i> "I have my own domain"</strong>
</p>
<p>
Domain: <span style="color: gray">@</span><strong><%= @site.username %>.neocities.org</strong>
</p>
<p>
TXT value:
</p>
<form method="POST" action="/settings/<%= @site.username %>/bluesky_set_did">
<%== csrf_token_input_html %>
<input name="did" type="text" style="width: 50%" placeholder="did=did:plc:somethingexamplesomething" value="<%= @bluesky_did %>">
<br>
<input class="btn-Action" type="submit" value="Update">
</form>

View file

@ -74,9 +74,9 @@
</p>
<% end %>
</div>
<%== erb :'_news', layout: false, locals: {site: @site, events: @latest_events} %>
<%== erb :'_news', layout: false, locals: {site: site, events: @latest_events} %>
<% else %>
<div class="site-profile-padding"><%== erb :'_news', layout: false, locals: {site: @site, events: @latest_events} %></div>
<div class="site-profile-padding"><%== erb :'_news', layout: false, locals: {site: site, events: @latest_events} %></div>
<% end %>
</div>

View file

@ -163,42 +163,47 @@
var editor = {}
$(document).ready(function() {
$.get("/site_files/download/<%= Addressable::URI.parse(@filename).normalized_path.to_s %>", function(resp) {
editor = ace.edit("editor")
setTheme()
<% if @ace_mode %>
editor.getSession().setMode("ace/mode/<%= @ace_mode %>")
<% end %>
editor.getSession().setTabSize(2)
editor.getSession().setUseWrapMode(true)
editor.setFontSize(14)
editor.setShowPrintMargin(false)
editor.setOptions({
maxLines: Infinity,
autoScrollEditorIntoView: true
})
$.ajax({
url: "/site_files/download/<%= Addressable::URI.parse(@filename).normalized_path.to_s %>",
cache: false,
success: function(resp) {
editor = ace.edit("editor")
setTheme()
<% if @ace_mode %>
editor.getSession().setMode("ace/mode/<%= @ace_mode %>")
<% end %>
editor.getSession().setTabSize(2)
editor.getSession().setUseWrapMode(true)
editor.setFontSize(14)
editor.setShowPrintMargin(false)
editor.setOptions({
maxLines: Infinity,
autoScrollEditorIntoView: true
})
// Disable autocomplete
editor.setBehavioursEnabled(false)
// Disable autocomplete
editor.setBehavioursEnabled(false)
editor.setValue(resp, -1)
editor.getSession().setUndoManager(new ace.UndoManager())
editor.setValue(resp, -1)
editor.getSession().setUndoManager(new ace.UndoManager())
editor.on('change', function(obj) {
$('a#saveButton,a#saveAndExitButton').css('opacity', 1)
unsavedChanges = true
})
editor.on('change', function(obj) {
$('a#saveButton,a#saveAndExitButton').css('opacity', 1)
unsavedChanges = true
})
editor.commands.addCommand({
name: 'saveCommand',
bindKey: {win: 'Ctrl-S', mac: 'Command-S'},
exec: function(editor) {
saveTextFile(false)
}
})
})
})
editor.commands.addCommand({
name: 'saveCommand',
bindKey: {win: 'Ctrl-S', mac: 'Command-S'},
exec: function(editor) {
saveTextFile(false)
}
})
}
});
});
window.onbeforeunload = function() {
if(unsavedChanges == true)

View file

@ -64,7 +64,11 @@ class LetsEncryptWorker
puts "testing #{challenge_url}"
begin
res = HTTP.timeout(connect: 10, write: 10, read: 10).follow.get(challenge_url)
# Some dumb letsencrypt related cert expiration issue hotfix
ctx = OpenSSL::SSL::SSLContext.new
ctx.verify_mode = OpenSSL::SSL::VERIFY_NONE
res = HTTP.timeout(connect: 10, write: 10, read: 10).follow.get(challenge_url, ssl_context: ctx)
rescue => e
puts e.inspect
puts "error with #{challenge_url}"

View file

@ -6,7 +6,7 @@ class ScreenshotWorker
HARD_TIMEOUT = 30.freeze
PAGE_WAIT_TIME = 5.freeze # 3D/VR sites take a bit to render after loading usually.
include Sidekiq::Worker
sidekiq_options queue: :screenshots, retry: 2, backtrace: true
sidekiq_options queue: :screenshots, retry: 10, backtrace: true
def perform(username, path)
site = Site[username: username]
@ -47,19 +47,21 @@ class ScreenshotWorker
base_image_tmpfile_path = "/tmp/#{SecureRandom.uuid}.png"
http_resp = HTTP.basic_auth(user: api_user, pass: api_password).get(uri)
BlackBox.new(site, path).check_uri(http_resp.headers['X-URL']) if defined?(BlackBox)
BlackBox.new(site, path).check_uri(http_resp.headers['X-URL']) if defined?(BlackBox) && http_resp.headers['X-URL']
File.write base_image_tmpfile_path, http_resp.to_s
user_screenshots_path = File.join SCREENSHOTS_PATH, Site.sharding_dir(username), username
screenshot_path = File.join user_screenshots_path, File.dirname(path_for_screenshot)
FileUtils.mkdir_p screenshot_path unless Dir.exist?(screenshot_path)
FileUtils.cp base_image_tmpfile_path, File.join(user_screenshots_path, "#{path_for_screenshot}.png")
Site::SCREENSHOT_RESOLUTIONS.each do |res|
width, height = res.split('x').collect {|r| r.to_i}
full_screenshot_path = File.join(user_screenshots_path, "#{path_for_screenshot}.#{res}.webp")
opts = {quality: 90, resize_w: width, resize_h: height}
opts = {resize_w: width, resize_h: height, near_lossless: 0}
if width == height
opts.merge! crop_x: 160, crop_y: 0, crop_w: 960, crop_h: 960
@ -69,6 +71,8 @@ class ScreenshotWorker
end
true
rescue WebP::EncoderError => e
puts "Failed: #{username} #{path} #{e.inspect}"
rescue => e
raise e
ensure

View file

@ -24,11 +24,20 @@ class ThumbnailWorker
format = File.extname(path).gsub('.', '')
full_thumbnail_path = File.join(user_thumbnails_path, "#{path}.#{res}.webp")
image = Rszr::Image.load site_file_path
if image.width > image.height
image.resize! width, :auto
else
image.resize! :auto, height
begin
image = Rszr::Image.load site_file_path
rescue Rszr::LoadError
next
end
begin
if image.width > image.height
image.resize! width, :auto
else
image.resize! :auto, height
end
rescue Rszr::TransformationError
next
end
begin