mirror of
https://github.com/neocities/neocities.git
synced 2026-05-26 05:34:53 +00:00
461 lines
12 KiB
Ruby
461 lines
12 KiB
Ruby
get '/admin' do
|
|
require_admin
|
|
@banned_sites = Site.select(:username).filter(is_banned: true).order(:username).all
|
|
@nsfw_sites = Site.select(:username).filter(is_nsfw: true).order(:username).all
|
|
erb :'admin'
|
|
end
|
|
|
|
get '/admin/reports' do
|
|
require_admin
|
|
@page = params[:page] ? params[:page].to_i : 1
|
|
@page = 1 if @page < 1
|
|
@per_page = 51
|
|
|
|
@moderation_blur_categories = $config['moderation_blur_categories']
|
|
|
|
@reports = Report.join(:sites, id: :site_id).where(sites__is_deleted: false, sites__is_banned: false).order(:reports__created_at.desc).select_all(:reports).paginate(@page, @per_page)
|
|
@pagination_dataset = @reports
|
|
|
|
erb :'admin/reports'
|
|
end
|
|
|
|
post '/admin/reports/:report_id/dismiss' do
|
|
require_admin
|
|
content_type :json
|
|
|
|
report = Report[params[:report_id]]
|
|
return {success: false, error: 'Report not found'}.to_json if report.nil?
|
|
|
|
report.destroy
|
|
|
|
# Dismiss any other reports for the site
|
|
DB[:reports].where(site_id: report.site_id).delete
|
|
|
|
{success: true, message: 'Report dismissed'}.to_json
|
|
end
|
|
|
|
post '/admin/site_files/train' do
|
|
require_admin
|
|
site = Site[params[:site_id]]
|
|
site_file = site.site_files_dataset.where(path: params[:path]).first
|
|
not_found if site_file.nil?
|
|
site.untrain site_file.path
|
|
site.train site_file.path, params[:classifier]
|
|
'ok'
|
|
end
|
|
|
|
get '/admin/usage' do
|
|
require_admin
|
|
today = Date.today
|
|
current_month = Date.new today.year, today.month, 1
|
|
start_month = Date.new(2016, 2, 1)
|
|
|
|
# Build date range for batch query
|
|
months = []
|
|
month = current_month
|
|
until month < start_month
|
|
months << month
|
|
month = month.prev_month
|
|
end
|
|
|
|
# Batch query for all monthly stats at once using proper date comparisons
|
|
monthly_stats_query = <<-SQL
|
|
SELECT
|
|
DATE_TRUNC('month', created_at) as month,
|
|
SUM(views) as views,
|
|
SUM(hits) as hits,
|
|
SUM(bandwidth) as bandwidth
|
|
FROM daily_site_stats
|
|
WHERE created_at >= ?
|
|
AND created_at <= ?
|
|
GROUP BY DATE_TRUNC('month', created_at)
|
|
ORDER BY month DESC
|
|
SQL
|
|
|
|
monthly_data = DB[monthly_stats_query, start_month, current_month.next_month].all
|
|
|
|
# Convert to hash for easy lookup
|
|
stats_by_month = {}
|
|
monthly_data.each do |row|
|
|
month_key = row[:month].to_date
|
|
stats_by_month[month_key] = {
|
|
views: row[:views] || 0,
|
|
hits: row[:hits] || 0,
|
|
bandwidth: row[:bandwidth] || 0
|
|
}
|
|
end
|
|
|
|
# Batch query for all popular sites data at once
|
|
popular_sites_query = <<-SQL
|
|
SELECT
|
|
DATE_TRUNC('month', stats.created_at) as month,
|
|
sites.username,
|
|
SUM(stats.bandwidth) as bandwidth
|
|
FROM stats
|
|
LEFT JOIN sites ON sites.id = stats.site_id
|
|
WHERE stats.created_at >= ?
|
|
AND stats.created_at <= ?
|
|
AND sites.username IS NOT NULL
|
|
GROUP BY DATE_TRUNC('month', stats.created_at), sites.username
|
|
SQL
|
|
|
|
all_popular_sites = DB[popular_sites_query, start_month, current_month.next_month].all
|
|
|
|
# Group popular sites by month and get top 50 for each
|
|
popular_by_month = {}
|
|
all_popular_sites.group_by { |row| row[:month].to_date }.each do |month_key, sites|
|
|
popular_by_month[month_key] = sites
|
|
.sort_by { |s| -(s[:bandwidth] || 0) }
|
|
.take(50)
|
|
.map { |s| { username: s[:username], bandwidth: s[:bandwidth] } }
|
|
end
|
|
|
|
# Combine the results
|
|
@monthly_stats = []
|
|
months.each do |month|
|
|
stats = stats_by_month[month]
|
|
next unless stats && stats[:views] != 0 && stats[:hits] != 0 && stats[:bandwidth] != 0
|
|
|
|
@monthly_stats.push({
|
|
views: stats[:views],
|
|
hits: stats[:hits],
|
|
bandwidth: stats[:bandwidth],
|
|
date: month,
|
|
popular_sites: popular_by_month[month] || []
|
|
})
|
|
end
|
|
|
|
erb :'admin/usage'
|
|
end
|
|
|
|
get '/admin/email' do
|
|
require_admin
|
|
erb :'admin/email'
|
|
end
|
|
|
|
get '/admin/stats' do
|
|
require_admin
|
|
@stats = {
|
|
total_hosted_site_hits: DB['SELECT SUM(hits) FROM sites'].first[:sum],
|
|
total_hosted_site_views: DB['SELECT SUM(views) FROM sites'].first[:sum],
|
|
total_site_changes: DB['select max(changed_count) from sites'].first[:max],
|
|
total_sites: Site.count
|
|
}
|
|
|
|
# Start with the date of the first created site
|
|
start = Site.select(:created_at).
|
|
exclude(created_at: nil).
|
|
order(:created_at).
|
|
first[:created_at].to_date
|
|
|
|
runner = start
|
|
monthly_stats = []
|
|
now = Date.today
|
|
|
|
while Date.new(runner.year, runner.month, 1) <= Date.new(now.year, now.month, 1)
|
|
# Get sites created in this time range
|
|
sites_in_range = Site.where(created_at: runner..runner.next_month)
|
|
sites_from_start = Site.where(created_at: start..runner.next_month)
|
|
|
|
supporters_count = Site.where(created_at: start..runner.next_month, parent_site_id: nil)
|
|
.where(
|
|
Sequel.|(
|
|
~{stripe_customer_id: nil} & ~{stripe_subscription_id: nil} & {plan_ended: [false, nil]} & ~{plan_type: ['free', 'special']},
|
|
{paypal_active: true}
|
|
)
|
|
).count
|
|
|
|
monthly_stats.push(
|
|
date: runner,
|
|
sites_created: sites_in_range.count,
|
|
total_from_start: sites_from_start.count,
|
|
supporters: supporters_count,
|
|
)
|
|
|
|
runner = runner.next_month
|
|
end
|
|
|
|
@stats[:monthly_stats] = monthly_stats
|
|
|
|
@stats[:current_supporters] = Site.where(parent_site_id: nil)
|
|
.where(
|
|
Sequel.|(
|
|
~{stripe_customer_id: nil} & ~{stripe_subscription_id: nil} & {plan_ended: [false, nil]} & ~{plan_type: ['free', 'special']},
|
|
{paypal_active: true}
|
|
)
|
|
).count
|
|
@stats[:supporter_percentage] = (@stats[:current_supporters].to_f / @stats[:total_sites] * 100).round(2)
|
|
|
|
erb :'admin/stats'
|
|
end
|
|
|
|
get '/admin/ban_history' do
|
|
require_admin
|
|
|
|
@title = 'Ban History'
|
|
@page = params[:page] ? params[:page].to_i : 1
|
|
@page = 1 if @page < 1
|
|
@per_page = 51
|
|
|
|
@sites = Site.where(is_banned: true).order(:banned_at.desc).exclude(banned_at: nil). paginate(@page, @per_page)
|
|
@pagination_dataset = @sites
|
|
|
|
erb :'admin/ban_history'
|
|
end
|
|
|
|
post '/admin/email' do
|
|
require_admin
|
|
%i{subject body}.each do |k|
|
|
if params[k].nil? || params[k].empty?
|
|
flash[:error] = "#{k.capitalize} is missing."
|
|
redirect '/admin/email'
|
|
end
|
|
end
|
|
|
|
sites = Site.newsletter_sites
|
|
|
|
day = 0
|
|
|
|
until sites.empty?
|
|
seconds = 0.0
|
|
queued_sites = []
|
|
Site::EMAIL_BLAST_MAXIMUM_PER_DAY.times {
|
|
break if sites.empty?
|
|
queued_sites << sites.pop
|
|
}
|
|
|
|
queued_sites.each do |site|
|
|
EmailWorker.perform_at((day.days.from_now + seconds), {
|
|
from: Site::FROM_EMAIL,
|
|
to: site.email,
|
|
subject: params[:subject],
|
|
body: params[:body]
|
|
})
|
|
seconds += 0.5
|
|
end
|
|
|
|
day += 1
|
|
end
|
|
|
|
flash[:success] = "#{sites.length} emails have been queued, #{Site::EMAIL_BLAST_MAXIMUM_PER_DAY} per day."
|
|
redirect '/'
|
|
end
|
|
|
|
post '/admin/ban' do
|
|
require_admin
|
|
if params[:usernames].empty?
|
|
flash[:error] = 'no usernames provided'
|
|
redirect request.referrer
|
|
end
|
|
|
|
usernames = params[:usernames].split("\n").collect {|u| u.strip}
|
|
|
|
deleted_count = 0
|
|
ip_deleted_count = 0
|
|
|
|
usernames.each do |username|
|
|
next if username == ''
|
|
site = Site[username: username]
|
|
next if site.nil? || site.is_banned
|
|
|
|
if !params[:classifier].empty?
|
|
site.untrain 'index.html'
|
|
site.train 'index.html', params[:classifier]
|
|
end
|
|
|
|
site.ban!
|
|
deleted_count += 1
|
|
|
|
if !params[:ban_using_ips].empty? && IPAddress.valid?(site.ip)
|
|
sites = Site.filter(ip: site.ip, is_banned: false).all
|
|
sites.each do |s|
|
|
next if usernames.include?(s.username)
|
|
s.ban!
|
|
end
|
|
ip_deleted_count += 1
|
|
end
|
|
|
|
if params[:classifier] == 'spam' || params[:classifier] == 'phishing'
|
|
next unless IPAddress.valid?(site.ip)
|
|
StopForumSpamWorker.perform_async(
|
|
username: site.username,
|
|
email: site.email,
|
|
ip: site.ip,
|
|
classifier: params[:classifier]
|
|
)
|
|
end
|
|
end
|
|
|
|
flash[:success] = "#{ip_deleted_count + deleted_count} sites have been banned, including #{ip_deleted_count} matching IPs."
|
|
redirect request.referrer
|
|
end
|
|
|
|
post '/admin/unban' do
|
|
require_admin
|
|
site = Site[username: params[:username]]
|
|
|
|
if site.nil?
|
|
flash[:error] = 'User not found'
|
|
redirect request.referrer
|
|
end
|
|
|
|
if !site.is_banned
|
|
flash[:error] = 'Site is not banned'
|
|
redirect request.referrer
|
|
end
|
|
|
|
site.unban!
|
|
|
|
flash[:success] = "Site #{site.username} was unbanned."
|
|
redirect request.referrer
|
|
end
|
|
|
|
post '/admin/mark_nsfw' do
|
|
require_admin
|
|
site = Site[username: params[:username]]
|
|
|
|
if site.nil?
|
|
flash[:error] = 'User not found'
|
|
redirect request.referrer
|
|
end
|
|
|
|
site.is_nsfw = true
|
|
site.admin_nsfw = true
|
|
site.save_changes validate: false
|
|
|
|
flash[:success] = 'MISSION ACCOMPLISHED'
|
|
redirect request.referrer
|
|
end
|
|
|
|
post '/admin/unmark_nsfw' do
|
|
require_admin
|
|
site = Site[username: params[:username]]
|
|
|
|
if site.nil?
|
|
flash[:error] = 'User not found'
|
|
redirect request.referrer
|
|
end
|
|
|
|
site.is_nsfw = false
|
|
site.admin_nsfw = false
|
|
site.save_changes validate: false
|
|
|
|
flash[:success] = 'Site unmarked as NSFW'
|
|
redirect request.referrer
|
|
end
|
|
|
|
post '/admin/mark_moderated' do
|
|
require_admin
|
|
|
|
not_found if params[:site_id].blank?
|
|
site = Site[params[:site_id]]
|
|
not_found if site.nil?
|
|
|
|
site_ids = Site.moderation_dataset.select(:id).where{created_at <= site.created_at}.all.collect {|s| s.id}
|
|
|
|
DB[:sites].where(id: site_ids).update(needs_moderation: false)
|
|
|
|
remaining_ds = Site.moderation_dataset.order(:created_at.desc)
|
|
remaining_ds = remaining_ds.paginate(1, Site::BROWSE_PAGINATION_LENGTH)
|
|
last_page = remaining_ds.page_count
|
|
last_page = 1 if last_page < 1
|
|
|
|
uri = Addressable::URI.parse request.referer
|
|
query = Rack::Utils.parse_query uri.query
|
|
query['page'] = last_page.to_s
|
|
uri.query = Rack::Utils.build_query(query)
|
|
redirect uri.to_s
|
|
end
|
|
|
|
post '/admin/feature' do
|
|
require_admin
|
|
site = Site[username: params[:username]]
|
|
|
|
if site.nil?
|
|
flash[:error] = 'User not found'
|
|
redirect request.referrer
|
|
end
|
|
|
|
site.featured_at = Time.now
|
|
site.save_changes(validate: false)
|
|
flash[:success] = 'Site has been featured.'
|
|
redirect request.referrer
|
|
end
|
|
|
|
post '/admin/verify_email' do
|
|
require_admin
|
|
site = Site[username: params[:username]]
|
|
|
|
if site.nil?
|
|
flash[:error] = 'User not found'
|
|
redirect request.referrer
|
|
end
|
|
|
|
if site.email_confirmed
|
|
flash[:error] = 'Email is already confirmed'
|
|
redirect request.referrer
|
|
end
|
|
|
|
site.email_confirmed = true
|
|
site.save_changes(validate: false)
|
|
flash[:success] = "Email for #{site.username} has been manually verified."
|
|
redirect request.referrer
|
|
end
|
|
|
|
get '/admin/masquerade/:username' do
|
|
require_admin
|
|
site = Site[username: params[:username]]
|
|
not_found if site.nil?
|
|
session[:id] = site.id
|
|
redirect '/'
|
|
end
|
|
|
|
get %r{/admin/site/(.+)} do |username_or_email_or_domain|
|
|
require_admin
|
|
ident = request.path_info.sub(%r{\A/admin/site/}, '')
|
|
|
|
if ident.blank?
|
|
flash[:error] = 'username or email or domain required'
|
|
redirect '/admin'
|
|
end
|
|
|
|
ident = ident.to_s.strip
|
|
ident = ident.sub(%r{\A(https?):/+}, '\1://')
|
|
|
|
begin
|
|
parsed_ident = Addressable::URI.parse(ident)
|
|
ident = parsed_ident.host if parsed_ident.host
|
|
rescue Addressable::URI::InvalidURIError
|
|
end
|
|
|
|
ident = ident.split('/').first.to_s.downcase
|
|
|
|
if ident =~ /@/
|
|
@site = Site[email: ident]
|
|
elsif ident.end_with?('.neocities.org')
|
|
@site = Site[username: ident.sub(/\.neocities\.org\z/, '')]
|
|
elsif ident =~ /.+\..+$/
|
|
@site = Site.where(domain: ident).first
|
|
else
|
|
@site = Site[username: ident]
|
|
end
|
|
|
|
if @site.nil?
|
|
flash[:error] = "site not found"
|
|
redirect request.referrer
|
|
end
|
|
|
|
@title = "Site Info - #{@site.username}"
|
|
@moderation_blur_categories = Array($config['moderation_blur_categories'])
|
|
@moderation_blur_paths = Report
|
|
.where(site_id: @site.id, type: @moderation_blur_categories)
|
|
.all
|
|
.map do |report|
|
|
path = report.site_file_path.to_s.sub(%r{\A/+}, '')
|
|
path.empty? ? 'index.html' : path
|
|
end
|
|
.uniq
|
|
|
|
erb :'admin/site'
|
|
end
|