commit
c9a316ad35
@ -30,10 +30,7 @@ require "./invidious/*"
|
|||||||
require "./invidious/routes/**"
|
require "./invidious/routes/**"
|
||||||
require "./invidious/jobs/**"
|
require "./invidious/jobs/**"
|
||||||
|
|
||||||
ENV_CONFIG_NAME = "INVIDIOUS_CONFIG"
|
CONFIG = Config.load
|
||||||
|
|
||||||
CONFIG_STR = ENV.has_key?(ENV_CONFIG_NAME) ? ENV.fetch(ENV_CONFIG_NAME) : File.read("config/config.yml")
|
|
||||||
CONFIG = Config.from_yaml(CONFIG_STR)
|
|
||||||
HMAC_KEY = CONFIG.hmac_key || Random::Secure.hex(32)
|
HMAC_KEY = CONFIG.hmac_key || Random::Secure.hex(32)
|
||||||
|
|
||||||
PG_URL = URI.new(
|
PG_URL = URI.new(
|
||||||
@ -52,7 +49,7 @@ PUBSUB_URL = URI.parse("https://pubsubhubbub.appspot.com")
|
|||||||
REDDIT_URL = URI.parse("https://www.reddit.com")
|
REDDIT_URL = URI.parse("https://www.reddit.com")
|
||||||
TEXTCAPTCHA_URL = URI.parse("https://textcaptcha.com")
|
TEXTCAPTCHA_URL = URI.parse("https://textcaptcha.com")
|
||||||
YT_URL = URI.parse("https://www.youtube.com")
|
YT_URL = URI.parse("https://www.youtube.com")
|
||||||
HOST_URL = make_host_url(CONFIG, Kemal.config)
|
HOST_URL = make_host_url(Kemal.config)
|
||||||
|
|
||||||
CHARS_SAFE = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_"
|
CHARS_SAFE = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_"
|
||||||
TEST_IDS = {"AgbeGFYluEA", "BaW_jenozKc", "a9LDPn-MO4I", "ddFvjfvPnqk", "iqKdEhx-dD4"}
|
TEST_IDS = {"AgbeGFYluEA", "BaW_jenozKc", "a9LDPn-MO4I", "ddFvjfvPnqk", "iqKdEhx-dD4"}
|
||||||
@ -145,8 +142,6 @@ end
|
|||||||
OUTPUT = CONFIG.output.upcase == "STDOUT" ? STDOUT : File.open(CONFIG.output, mode: "a")
|
OUTPUT = CONFIG.output.upcase == "STDOUT" ? STDOUT : File.open(CONFIG.output, mode: "a")
|
||||||
LOGGER = Invidious::LogHandler.new(OUTPUT, CONFIG.log_level)
|
LOGGER = Invidious::LogHandler.new(OUTPUT, CONFIG.log_level)
|
||||||
|
|
||||||
config = CONFIG
|
|
||||||
|
|
||||||
# Check table integrity
|
# Check table integrity
|
||||||
if CONFIG.check_tables
|
if CONFIG.check_tables
|
||||||
check_enum(PG_DB, "privacy", PlaylistPrivacy)
|
check_enum(PG_DB, "privacy", PlaylistPrivacy)
|
||||||
@ -167,28 +162,33 @@ end
|
|||||||
|
|
||||||
# Start jobs
|
# Start jobs
|
||||||
|
|
||||||
Invidious::Jobs.register Invidious::Jobs::RefreshChannelsJob.new(PG_DB, config)
|
if CONFIG.channel_threads > 0
|
||||||
Invidious::Jobs.register Invidious::Jobs::RefreshFeedsJob.new(PG_DB, config)
|
Invidious::Jobs.register Invidious::Jobs::RefreshChannelsJob.new(PG_DB)
|
||||||
|
end
|
||||||
|
|
||||||
|
if CONFIG.feed_threads > 0
|
||||||
|
Invidious::Jobs.register Invidious::Jobs::RefreshFeedsJob.new(PG_DB)
|
||||||
|
end
|
||||||
|
|
||||||
DECRYPT_FUNCTION = DecryptFunction.new(CONFIG.decrypt_polling)
|
DECRYPT_FUNCTION = DecryptFunction.new(CONFIG.decrypt_polling)
|
||||||
if config.decrypt_polling
|
if CONFIG.decrypt_polling
|
||||||
Invidious::Jobs.register Invidious::Jobs::UpdateDecryptFunctionJob.new
|
Invidious::Jobs.register Invidious::Jobs::UpdateDecryptFunctionJob.new
|
||||||
end
|
end
|
||||||
|
|
||||||
if config.statistics_enabled
|
if CONFIG.statistics_enabled
|
||||||
Invidious::Jobs.register Invidious::Jobs::StatisticsRefreshJob.new(PG_DB, config, SOFTWARE)
|
Invidious::Jobs.register Invidious::Jobs::StatisticsRefreshJob.new(PG_DB, SOFTWARE)
|
||||||
end
|
end
|
||||||
|
|
||||||
if (config.use_pubsub_feeds.is_a?(Bool) && config.use_pubsub_feeds.as(Bool)) || (config.use_pubsub_feeds.is_a?(Int32) && config.use_pubsub_feeds.as(Int32) > 0)
|
if (CONFIG.use_pubsub_feeds.is_a?(Bool) && CONFIG.use_pubsub_feeds.as(Bool)) || (CONFIG.use_pubsub_feeds.is_a?(Int32) && CONFIG.use_pubsub_feeds.as(Int32) > 0)
|
||||||
Invidious::Jobs.register Invidious::Jobs::SubscribeToFeedsJob.new(PG_DB, config, HMAC_KEY)
|
Invidious::Jobs.register Invidious::Jobs::SubscribeToFeedsJob.new(PG_DB, HMAC_KEY)
|
||||||
end
|
end
|
||||||
|
|
||||||
if config.popular_enabled
|
if CONFIG.popular_enabled
|
||||||
Invidious::Jobs.register Invidious::Jobs::PullPopularVideosJob.new(PG_DB)
|
Invidious::Jobs.register Invidious::Jobs::PullPopularVideosJob.new(PG_DB)
|
||||||
end
|
end
|
||||||
|
|
||||||
if config.captcha_key
|
if CONFIG.captcha_key
|
||||||
Invidious::Jobs.register Invidious::Jobs::BypassCaptchaJob.new(config)
|
Invidious::Jobs.register Invidious::Jobs::BypassCaptchaJob.new
|
||||||
end
|
end
|
||||||
|
|
||||||
connection_channel = Channel({Bool, Channel(PQ::Notification)}).new(32)
|
connection_channel = Channel({Bool, Channel(PQ::Notification)}).new(32)
|
||||||
@ -219,7 +219,7 @@ before_all do |env|
|
|||||||
env.response.headers["Content-Security-Policy"] = "default-src 'none'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; font-src 'self' data:; connect-src 'self'; manifest-src 'self'; media-src 'self' blob:#{extra_media_csp}"
|
env.response.headers["Content-Security-Policy"] = "default-src 'none'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; font-src 'self' data:; connect-src 'self'; manifest-src 'self'; media-src 'self' blob:#{extra_media_csp}"
|
||||||
env.response.headers["Referrer-Policy"] = "same-origin"
|
env.response.headers["Referrer-Policy"] = "same-origin"
|
||||||
|
|
||||||
if (Kemal.config.ssl || config.https_only) && config.hsts
|
if (Kemal.config.ssl || CONFIG.https_only) && CONFIG.hsts
|
||||||
env.response.headers["Strict-Transport-Security"] = "max-age=31536000; includeSubDomains; preload"
|
env.response.headers["Strict-Transport-Security"] = "max-age=31536000; includeSubDomains; preload"
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -1164,7 +1164,7 @@ end
|
|||||||
get "/feed/popular" do |env|
|
get "/feed/popular" do |env|
|
||||||
locale = LOCALES[env.get("preferences").as(Preferences).locale]?
|
locale = LOCALES[env.get("preferences").as(Preferences).locale]?
|
||||||
|
|
||||||
if config.popular_enabled
|
if CONFIG.popular_enabled
|
||||||
templated "popular"
|
templated "popular"
|
||||||
else
|
else
|
||||||
message = translate(locale, "The Popular feed has been disabled by the administrator.")
|
message = translate(locale, "The Popular feed has been disabled by the administrator.")
|
||||||
@ -1822,7 +1822,7 @@ get "/api/v1/stats" do |env|
|
|||||||
locale = LOCALES[env.get("preferences").as(Preferences).locale]?
|
locale = LOCALES[env.get("preferences").as(Preferences).locale]?
|
||||||
env.response.content_type = "application/json"
|
env.response.content_type = "application/json"
|
||||||
|
|
||||||
if !config.statistics_enabled
|
if !CONFIG.statistics_enabled
|
||||||
next error_json(400, "Statistics are not enabled.")
|
next error_json(400, "Statistics are not enabled.")
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -2232,7 +2232,7 @@ get "/api/v1/popular" do |env|
|
|||||||
|
|
||||||
env.response.content_type = "application/json"
|
env.response.content_type = "application/json"
|
||||||
|
|
||||||
if !config.popular_enabled
|
if !CONFIG.popular_enabled
|
||||||
error_message = {"error" => "Administrator has disabled this endpoint."}.to_json
|
error_message = {"error" => "Administrator has disabled this endpoint."}.to_json
|
||||||
env.response.status_code = 400
|
env.response.status_code = 400
|
||||||
next error_message
|
next error_message
|
||||||
|
@ -7,7 +7,7 @@ class InfoException < Exception
|
|||||||
end
|
end
|
||||||
|
|
||||||
macro error_template(*args)
|
macro error_template(*args)
|
||||||
error_template_helper(env, config, locale, {{*args}})
|
error_template_helper(env, locale, {{*args}})
|
||||||
end
|
end
|
||||||
|
|
||||||
def github_details(summary : String, content : String)
|
def github_details(summary : String, content : String)
|
||||||
@ -22,9 +22,9 @@ def github_details(summary : String, content : String)
|
|||||||
return HTML.escape(details)
|
return HTML.escape(details)
|
||||||
end
|
end
|
||||||
|
|
||||||
def error_template_helper(env : HTTP::Server::Context, config : Config, locale : Hash(String, JSON::Any) | Nil, status_code : Int32, exception : Exception)
|
def error_template_helper(env : HTTP::Server::Context, locale : Hash(String, JSON::Any) | Nil, status_code : Int32, exception : Exception)
|
||||||
if exception.is_a?(InfoException)
|
if exception.is_a?(InfoException)
|
||||||
return error_template_helper(env, config, locale, status_code, exception.message || "")
|
return error_template_helper(env, locale, status_code, exception.message || "")
|
||||||
end
|
end
|
||||||
env.response.content_type = "text/html"
|
env.response.content_type = "text/html"
|
||||||
env.response.status_code = status_code
|
env.response.status_code = status_code
|
||||||
@ -43,7 +43,7 @@ def error_template_helper(env : HTTP::Server::Context, config : Config, locale :
|
|||||||
return templated "error"
|
return templated "error"
|
||||||
end
|
end
|
||||||
|
|
||||||
def error_template_helper(env : HTTP::Server::Context, config : Config, locale : Hash(String, JSON::Any) | Nil, status_code : Int32, message : String)
|
def error_template_helper(env : HTTP::Server::Context, locale : Hash(String, JSON::Any) | Nil, status_code : Int32, message : String)
|
||||||
env.response.content_type = "text/html"
|
env.response.content_type = "text/html"
|
||||||
env.response.status_code = status_code
|
env.response.status_code = status_code
|
||||||
error_message = translate(locale, message)
|
error_message = translate(locale, message)
|
||||||
@ -51,31 +51,31 @@ def error_template_helper(env : HTTP::Server::Context, config : Config, locale :
|
|||||||
end
|
end
|
||||||
|
|
||||||
macro error_atom(*args)
|
macro error_atom(*args)
|
||||||
error_atom_helper(env, config, locale, {{*args}})
|
error_atom_helper(env, locale, {{*args}})
|
||||||
end
|
end
|
||||||
|
|
||||||
def error_atom_helper(env : HTTP::Server::Context, config : Config, locale : Hash(String, JSON::Any) | Nil, status_code : Int32, exception : Exception)
|
def error_atom_helper(env : HTTP::Server::Context, locale : Hash(String, JSON::Any) | Nil, status_code : Int32, exception : Exception)
|
||||||
if exception.is_a?(InfoException)
|
if exception.is_a?(InfoException)
|
||||||
return error_atom_helper(env, config, locale, status_code, exception.message || "")
|
return error_atom_helper(env, locale, status_code, exception.message || "")
|
||||||
end
|
end
|
||||||
env.response.content_type = "application/atom+xml"
|
env.response.content_type = "application/atom+xml"
|
||||||
env.response.status_code = status_code
|
env.response.status_code = status_code
|
||||||
return "<error>#{exception.inspect_with_backtrace}</error>"
|
return "<error>#{exception.inspect_with_backtrace}</error>"
|
||||||
end
|
end
|
||||||
|
|
||||||
def error_atom_helper(env : HTTP::Server::Context, config : Config, locale : Hash(String, JSON::Any) | Nil, status_code : Int32, message : String)
|
def error_atom_helper(env : HTTP::Server::Context, locale : Hash(String, JSON::Any) | Nil, status_code : Int32, message : String)
|
||||||
env.response.content_type = "application/atom+xml"
|
env.response.content_type = "application/atom+xml"
|
||||||
env.response.status_code = status_code
|
env.response.status_code = status_code
|
||||||
return "<error>#{message}</error>"
|
return "<error>#{message}</error>"
|
||||||
end
|
end
|
||||||
|
|
||||||
macro error_json(*args)
|
macro error_json(*args)
|
||||||
error_json_helper(env, config, locale, {{*args}})
|
error_json_helper(env, locale, {{*args}})
|
||||||
end
|
end
|
||||||
|
|
||||||
def error_json_helper(env : HTTP::Server::Context, config : Config, locale : Hash(String, JSON::Any) | Nil, status_code : Int32, exception : Exception, additional_fields : Hash(String, Object) | Nil)
|
def error_json_helper(env : HTTP::Server::Context, locale : Hash(String, JSON::Any) | Nil, status_code : Int32, exception : Exception, additional_fields : Hash(String, Object) | Nil)
|
||||||
if exception.is_a?(InfoException)
|
if exception.is_a?(InfoException)
|
||||||
return error_json_helper(env, config, locale, status_code, exception.message || "", additional_fields)
|
return error_json_helper(env, locale, status_code, exception.message || "", additional_fields)
|
||||||
end
|
end
|
||||||
env.response.content_type = "application/json"
|
env.response.content_type = "application/json"
|
||||||
env.response.status_code = status_code
|
env.response.status_code = status_code
|
||||||
@ -86,11 +86,11 @@ def error_json_helper(env : HTTP::Server::Context, config : Config, locale : Has
|
|||||||
return error_message.to_json
|
return error_message.to_json
|
||||||
end
|
end
|
||||||
|
|
||||||
def error_json_helper(env : HTTP::Server::Context, config : Config, locale : Hash(String, JSON::Any) | Nil, status_code : Int32, exception : Exception)
|
def error_json_helper(env : HTTP::Server::Context, locale : Hash(String, JSON::Any) | Nil, status_code : Int32, exception : Exception)
|
||||||
return error_json_helper(env, config, locale, status_code, exception, nil)
|
return error_json_helper(env, locale, status_code, exception, nil)
|
||||||
end
|
end
|
||||||
|
|
||||||
def error_json_helper(env : HTTP::Server::Context, config : Config, locale : Hash(String, JSON::Any) | Nil, status_code : Int32, message : String, additional_fields : Hash(String, Object) | Nil)
|
def error_json_helper(env : HTTP::Server::Context, locale : Hash(String, JSON::Any) | Nil, status_code : Int32, message : String, additional_fields : Hash(String, Object) | Nil)
|
||||||
env.response.content_type = "application/json"
|
env.response.content_type = "application/json"
|
||||||
env.response.status_code = status_code
|
env.response.status_code = status_code
|
||||||
error_message = {"error" => message}
|
error_message = {"error" => message}
|
||||||
@ -100,6 +100,6 @@ def error_json_helper(env : HTTP::Server::Context, config : Config, locale : Has
|
|||||||
return error_message.to_json
|
return error_message.to_json
|
||||||
end
|
end
|
||||||
|
|
||||||
def error_json_helper(env : HTTP::Server::Context, config : Config, locale : Hash(String, JSON::Any) | Nil, status_code : Int32, message : String)
|
def error_json_helper(env : HTTP::Server::Context, locale : Hash(String, JSON::Any) | Nil, status_code : Int32, message : String)
|
||||||
error_json_helper(env, config, locale, status_code, message, nil)
|
error_json_helper(env, locale, status_code, message, nil)
|
||||||
end
|
end
|
||||||
|
@ -115,6 +115,63 @@ class Config
|
|||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def self.load
|
||||||
|
# Load config from file or YAML string env var
|
||||||
|
env_config_file = "INVIDIOUS_CONFIG_FILE"
|
||||||
|
env_config_yaml = "INVIDIOUS_CONFIG"
|
||||||
|
|
||||||
|
config_file = ENV.has_key?(env_config_file) ? ENV.fetch(env_config_file) : "config/config.yml"
|
||||||
|
config_yaml = ENV.has_key?(env_config_yaml) ? ENV.fetch(env_config_yaml) : File.read(config_file)
|
||||||
|
|
||||||
|
config = Config.from_yaml(config_yaml)
|
||||||
|
|
||||||
|
# Update config from env vars (upcased and prefixed with "INVIDIOUS_")
|
||||||
|
{% for ivar in Config.instance_vars %}
|
||||||
|
{% env_id = "INVIDIOUS_#{ivar.id.upcase}" %}
|
||||||
|
|
||||||
|
if ENV.has_key?({{env_id}})
|
||||||
|
# puts %(Config.{{ivar.id}} : Loading from env var {{env_id}})
|
||||||
|
env_value = ENV.fetch({{env_id}})
|
||||||
|
success = false
|
||||||
|
|
||||||
|
# Use YAML converter if specified
|
||||||
|
{% ann = ivar.annotation(::YAML::Field) %}
|
||||||
|
{% if ann && ann[:converter] %}
|
||||||
|
puts %(Config.{{ivar.id}} : Parsing "#{env_value}" as {{ivar.type}} with {{ann[:converter]}} converter)
|
||||||
|
config.{{ivar.id}} = {{ann[:converter]}}.from_yaml(YAML::ParseContext.new, YAML::Nodes.parse(ENV.fetch({{env_id}})).nodes[0])
|
||||||
|
puts %(Config.{{ivar.id}} : Set to #{config.{{ivar.id}}})
|
||||||
|
success = true
|
||||||
|
|
||||||
|
# Use regular YAML parser otherwise
|
||||||
|
{% else %}
|
||||||
|
{% ivar_types = ivar.type.union? ? ivar.type.union_types : [ivar.type] %}
|
||||||
|
# Sort types to avoid parsing nulls and numbers as strings
|
||||||
|
{% ivar_types = ivar_types.sort_by { |ivar_type| ivar_type == Nil ? 0 : ivar_type == Int32 ? 1 : 2 } %}
|
||||||
|
{{ivar_types}}.each do |ivar_type|
|
||||||
|
if !success
|
||||||
|
begin
|
||||||
|
# puts %(Config.{{ivar.id}} : Trying to parse "#{env_value}" as #{ivar_type})
|
||||||
|
config.{{ivar.id}} = ivar_type.from_yaml(env_value)
|
||||||
|
puts %(Config.{{ivar.id}} : Set to #{config.{{ivar.id}}} (#{ivar_type}))
|
||||||
|
success = true
|
||||||
|
rescue
|
||||||
|
# nop
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
{% end %}
|
||||||
|
|
||||||
|
# Exit on fail
|
||||||
|
if !success
|
||||||
|
puts %(Config.{{ivar.id}} failed to parse #{env_value} as {{ivar.type}})
|
||||||
|
exit(1)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
{% end %}
|
||||||
|
|
||||||
|
return config
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
struct DBConfig
|
struct DBConfig
|
||||||
|
@ -280,9 +280,9 @@ def arg_array(array, start = 1)
|
|||||||
return args
|
return args
|
||||||
end
|
end
|
||||||
|
|
||||||
def make_host_url(config, kemal_config)
|
def make_host_url(kemal_config)
|
||||||
ssl = config.https_only || kemal_config.ssl
|
ssl = CONFIG.https_only || kemal_config.ssl
|
||||||
port = config.external_port || kemal_config.port
|
port = CONFIG.external_port || kemal_config.port
|
||||||
|
|
||||||
if ssl
|
if ssl
|
||||||
scheme = "https://"
|
scheme = "https://"
|
||||||
@ -297,11 +297,11 @@ def make_host_url(config, kemal_config)
|
|||||||
port = ""
|
port = ""
|
||||||
end
|
end
|
||||||
|
|
||||||
if !config.domain
|
if !CONFIG.domain
|
||||||
return ""
|
return ""
|
||||||
end
|
end
|
||||||
|
|
||||||
host = config.domain.not_nil!.lchop(".")
|
host = CONFIG.domain.not_nil!.lchop(".")
|
||||||
|
|
||||||
return "#{scheme}#{host}#{port}"
|
return "#{scheme}#{host}#{port}"
|
||||||
end
|
end
|
||||||
@ -345,7 +345,7 @@ def sha256(text)
|
|||||||
return digest.final.hexstring
|
return digest.final.hexstring
|
||||||
end
|
end
|
||||||
|
|
||||||
def subscribe_pubsub(topic, key, config)
|
def subscribe_pubsub(topic, key)
|
||||||
case topic
|
case topic
|
||||||
when .match(/^UC[A-Za-z0-9_-]{22}$/)
|
when .match(/^UC[A-Za-z0-9_-]{22}$/)
|
||||||
topic = "channel_id=#{topic}"
|
topic = "channel_id=#{topic}"
|
||||||
|
@ -1,9 +1,4 @@
|
|||||||
class Invidious::Jobs::BypassCaptchaJob < Invidious::Jobs::BaseJob
|
class Invidious::Jobs::BypassCaptchaJob < Invidious::Jobs::BaseJob
|
||||||
private getter config : Config
|
|
||||||
|
|
||||||
def initialize(@config)
|
|
||||||
end
|
|
||||||
|
|
||||||
def begin
|
def begin
|
||||||
loop do
|
loop do
|
||||||
begin
|
begin
|
||||||
@ -22,9 +17,9 @@ class Invidious::Jobs::BypassCaptchaJob < Invidious::Jobs::BaseJob
|
|||||||
|
|
||||||
headers = response.cookies.add_request_headers(HTTP::Headers.new)
|
headers = response.cookies.add_request_headers(HTTP::Headers.new)
|
||||||
|
|
||||||
response = JSON.parse(HTTP::Client.post(config.captcha_api_url + "/createTask",
|
response = JSON.parse(HTTP::Client.post(CONFIG.captcha_api_url + "/createTask",
|
||||||
headers: HTTP::Headers{"Content-Type" => "application/json"}, body: {
|
headers: HTTP::Headers{"Content-Type" => "application/json"}, body: {
|
||||||
"clientKey" => config.captcha_key,
|
"clientKey" => CONFIG.captcha_key,
|
||||||
"task" => {
|
"task" => {
|
||||||
"type" => "NoCaptchaTaskProxyless",
|
"type" => "NoCaptchaTaskProxyless",
|
||||||
"websiteURL" => "https://www.youtube.com#{path}",
|
"websiteURL" => "https://www.youtube.com#{path}",
|
||||||
@ -39,9 +34,9 @@ class Invidious::Jobs::BypassCaptchaJob < Invidious::Jobs::BaseJob
|
|||||||
loop do
|
loop do
|
||||||
sleep 10.seconds
|
sleep 10.seconds
|
||||||
|
|
||||||
response = JSON.parse(HTTP::Client.post(config.captcha_api_url + "/getTaskResult",
|
response = JSON.parse(HTTP::Client.post(CONFIG.captcha_api_url + "/getTaskResult",
|
||||||
headers: HTTP::Headers{"Content-Type" => "application/json"}, body: {
|
headers: HTTP::Headers{"Content-Type" => "application/json"}, body: {
|
||||||
"clientKey" => config.captcha_key,
|
"clientKey" => CONFIG.captcha_key,
|
||||||
"taskId" => task_id,
|
"taskId" => task_id,
|
||||||
}.to_json).body)
|
}.to_json).body)
|
||||||
|
|
||||||
@ -58,10 +53,10 @@ class Invidious::Jobs::BypassCaptchaJob < Invidious::Jobs::BaseJob
|
|||||||
|
|
||||||
response.cookies
|
response.cookies
|
||||||
.select { |cookie| cookie.name != "PREF" }
|
.select { |cookie| cookie.name != "PREF" }
|
||||||
.each { |cookie| config.cookies << cookie }
|
.each { |cookie| CONFIG.cookies << cookie }
|
||||||
|
|
||||||
# Persist cookies between runs
|
# Persist cookies between runs
|
||||||
File.write("config/config.yml", config.to_yaml)
|
File.write("config/config.yml", CONFIG.to_yaml)
|
||||||
elsif response.headers["Location"]?.try &.includes?("/sorry/index")
|
elsif response.headers["Location"]?.try &.includes?("/sorry/index")
|
||||||
location = response.headers["Location"].try { |u| URI.parse(u) }
|
location = response.headers["Location"].try { |u| URI.parse(u) }
|
||||||
headers = HTTP::Headers{":authority" => location.host.not_nil!}
|
headers = HTTP::Headers{":authority" => location.host.not_nil!}
|
||||||
@ -77,11 +72,11 @@ class Invidious::Jobs::BypassCaptchaJob < Invidious::Jobs::BaseJob
|
|||||||
inputs[node["name"]] = node["value"]
|
inputs[node["name"]] = node["value"]
|
||||||
end
|
end
|
||||||
|
|
||||||
captcha_client = HTTPClient.new(URI.parse(config.captcha_api_url))
|
captcha_client = HTTPClient.new(URI.parse(CONFIG.captcha_api_url))
|
||||||
captcha_client.family = config.force_resolve || Socket::Family::INET
|
captcha_client.family = CONFIG.force_resolve || Socket::Family::INET
|
||||||
response = JSON.parse(captcha_client.post("/createTask",
|
response = JSON.parse(captcha_client.post("/createTask",
|
||||||
headers: HTTP::Headers{"Content-Type" => "application/json"}, body: {
|
headers: HTTP::Headers{"Content-Type" => "application/json"}, body: {
|
||||||
"clientKey" => config.captcha_key,
|
"clientKey" => CONFIG.captcha_key,
|
||||||
"task" => {
|
"task" => {
|
||||||
"type" => "NoCaptchaTaskProxyless",
|
"type" => "NoCaptchaTaskProxyless",
|
||||||
"websiteURL" => location.to_s,
|
"websiteURL" => location.to_s,
|
||||||
@ -100,7 +95,7 @@ class Invidious::Jobs::BypassCaptchaJob < Invidious::Jobs::BaseJob
|
|||||||
|
|
||||||
response = JSON.parse(captcha_client.post("/getTaskResult",
|
response = JSON.parse(captcha_client.post("/getTaskResult",
|
||||||
headers: HTTP::Headers{"Content-Type" => "application/json"}, body: {
|
headers: HTTP::Headers{"Content-Type" => "application/json"}, body: {
|
||||||
"clientKey" => config.captcha_key,
|
"clientKey" => CONFIG.captcha_key,
|
||||||
"taskId" => task_id,
|
"taskId" => task_id,
|
||||||
}.to_json).body)
|
}.to_json).body)
|
||||||
|
|
||||||
@ -119,10 +114,10 @@ class Invidious::Jobs::BypassCaptchaJob < Invidious::Jobs::BaseJob
|
|||||||
}
|
}
|
||||||
cookies = HTTP::Cookies.from_headers(headers)
|
cookies = HTTP::Cookies.from_headers(headers)
|
||||||
|
|
||||||
cookies.each { |cookie| config.cookies << cookie }
|
cookies.each { |cookie| CONFIG.cookies << cookie }
|
||||||
|
|
||||||
# Persist cookies between runs
|
# Persist cookies between runs
|
||||||
File.write("config/config.yml", config.to_yaml)
|
File.write("config/config.yml", CONFIG.to_yaml)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
rescue ex
|
rescue ex
|
||||||
|
@ -1,12 +1,11 @@
|
|||||||
class Invidious::Jobs::RefreshChannelsJob < Invidious::Jobs::BaseJob
|
class Invidious::Jobs::RefreshChannelsJob < Invidious::Jobs::BaseJob
|
||||||
private getter db : DB::Database
|
private getter db : DB::Database
|
||||||
private getter config : Config
|
|
||||||
|
|
||||||
def initialize(@db, @config)
|
def initialize(@db)
|
||||||
end
|
end
|
||||||
|
|
||||||
def begin
|
def begin
|
||||||
max_fibers = config.channel_threads
|
max_fibers = CONFIG.channel_threads
|
||||||
lim_fibers = max_fibers
|
lim_fibers = max_fibers
|
||||||
active_fibers = 0
|
active_fibers = 0
|
||||||
active_channel = Channel(Bool).new
|
active_channel = Channel(Bool).new
|
||||||
@ -31,7 +30,7 @@ class Invidious::Jobs::RefreshChannelsJob < Invidious::Jobs::BaseJob
|
|||||||
spawn do
|
spawn do
|
||||||
begin
|
begin
|
||||||
LOGGER.trace("RefreshChannelsJob: #{id} fiber : Fetching channel")
|
LOGGER.trace("RefreshChannelsJob: #{id} fiber : Fetching channel")
|
||||||
channel = fetch_channel(id, db, config.full_refresh)
|
channel = fetch_channel(id, db, CONFIG.full_refresh)
|
||||||
|
|
||||||
lim_fibers = max_fibers
|
lim_fibers = max_fibers
|
||||||
|
|
||||||
|
@ -1,12 +1,11 @@
|
|||||||
class Invidious::Jobs::RefreshFeedsJob < Invidious::Jobs::BaseJob
|
class Invidious::Jobs::RefreshFeedsJob < Invidious::Jobs::BaseJob
|
||||||
private getter db : DB::Database
|
private getter db : DB::Database
|
||||||
private getter config : Config
|
|
||||||
|
|
||||||
def initialize(@db, @config)
|
def initialize(@db)
|
||||||
end
|
end
|
||||||
|
|
||||||
def begin
|
def begin
|
||||||
max_fibers = config.feed_threads
|
max_fibers = CONFIG.feed_threads
|
||||||
active_fibers = 0
|
active_fibers = 0
|
||||||
active_channel = Channel(Bool).new
|
active_channel = Channel(Bool).new
|
||||||
|
|
||||||
|
@ -21,9 +21,8 @@ class Invidious::Jobs::StatisticsRefreshJob < Invidious::Jobs::BaseJob
|
|||||||
}
|
}
|
||||||
|
|
||||||
private getter db : DB::Database
|
private getter db : DB::Database
|
||||||
private getter config : Config
|
|
||||||
|
|
||||||
def initialize(@db, @config, @software_config : Hash(String, String))
|
def initialize(@db, @software_config : Hash(String, String))
|
||||||
end
|
end
|
||||||
|
|
||||||
def begin
|
def begin
|
||||||
@ -43,7 +42,7 @@ class Invidious::Jobs::StatisticsRefreshJob < Invidious::Jobs::BaseJob
|
|||||||
"version" => @software_config["version"],
|
"version" => @software_config["version"],
|
||||||
"branch" => @software_config["branch"],
|
"branch" => @software_config["branch"],
|
||||||
}
|
}
|
||||||
STATISTICS["openRegistration"] = config.registration_enabled
|
STATISTICS["openRegistration"] = CONFIG.registration_enabled
|
||||||
end
|
end
|
||||||
|
|
||||||
private def refresh_stats
|
private def refresh_stats
|
||||||
|
@ -1,15 +1,14 @@
|
|||||||
class Invidious::Jobs::SubscribeToFeedsJob < Invidious::Jobs::BaseJob
|
class Invidious::Jobs::SubscribeToFeedsJob < Invidious::Jobs::BaseJob
|
||||||
private getter db : DB::Database
|
private getter db : DB::Database
|
||||||
private getter hmac_key : String
|
private getter hmac_key : String
|
||||||
private getter config : Config
|
|
||||||
|
|
||||||
def initialize(@db, @config, @hmac_key)
|
def initialize(@db, @hmac_key)
|
||||||
end
|
end
|
||||||
|
|
||||||
def begin
|
def begin
|
||||||
max_fibers = 1
|
max_fibers = 1
|
||||||
if config.use_pubsub_feeds.is_a?(Int32)
|
if CONFIG.use_pubsub_feeds.is_a?(Int32)
|
||||||
max_fibers = config.use_pubsub_feeds.as(Int32)
|
max_fibers = CONFIG.use_pubsub_feeds.as(Int32)
|
||||||
end
|
end
|
||||||
|
|
||||||
active_fibers = 0
|
active_fibers = 0
|
||||||
@ -30,7 +29,7 @@ class Invidious::Jobs::SubscribeToFeedsJob < Invidious::Jobs::BaseJob
|
|||||||
|
|
||||||
spawn do
|
spawn do
|
||||||
begin
|
begin
|
||||||
response = subscribe_pubsub(ucid, hmac_key, config)
|
response = subscribe_pubsub(ucid, hmac_key)
|
||||||
|
|
||||||
if response.status_code >= 400
|
if response.status_code >= 400
|
||||||
LOGGER.error("SubscribeToFeedsJob: #{ucid} : #{response.body}")
|
LOGGER.error("SubscribeToFeedsJob: #{ucid} : #{response.body}")
|
||||||
|
@ -1,6 +1,2 @@
|
|||||||
abstract class Invidious::Routes::BaseRoute
|
abstract class Invidious::Routes::BaseRoute
|
||||||
private getter config : Config
|
|
||||||
|
|
||||||
def initialize(@config)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
@ -6,7 +6,7 @@ class Invidious::Routes::Login < Invidious::Routes::BaseRoute
|
|||||||
|
|
||||||
return env.redirect "/feed/subscriptions" if user
|
return env.redirect "/feed/subscriptions" if user
|
||||||
|
|
||||||
if !config.login_enabled
|
if !CONFIG.login_enabled
|
||||||
return error_template(400, "Login has been disabled by administrator.")
|
return error_template(400, "Login has been disabled by administrator.")
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -33,7 +33,7 @@ class Invidious::Routes::Login < Invidious::Routes::BaseRoute
|
|||||||
|
|
||||||
referer = get_referer(env, "/feed/subscriptions")
|
referer = get_referer(env, "/feed/subscriptions")
|
||||||
|
|
||||||
if !config.login_enabled
|
if !CONFIG.login_enabled
|
||||||
return error_template(403, "Login has been disabled by administrator.")
|
return error_template(403, "Login has been disabled by administrator.")
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -274,14 +274,14 @@ class Invidious::Routes::Login < Invidious::Routes::BaseRoute
|
|||||||
|
|
||||||
host = URI.parse(env.request.headers["Host"]).host
|
host = URI.parse(env.request.headers["Host"]).host
|
||||||
|
|
||||||
if Kemal.config.ssl || config.https_only
|
if Kemal.config.ssl || CONFIG.https_only
|
||||||
secure = true
|
secure = true
|
||||||
else
|
else
|
||||||
secure = false
|
secure = false
|
||||||
end
|
end
|
||||||
|
|
||||||
cookies.each do |cookie|
|
cookies.each do |cookie|
|
||||||
if Kemal.config.ssl || config.https_only
|
if Kemal.config.ssl || CONFIG.https_only
|
||||||
cookie.secure = secure
|
cookie.secure = secure
|
||||||
else
|
else
|
||||||
cookie.secure = secure
|
cookie.secure = secure
|
||||||
@ -330,14 +330,14 @@ class Invidious::Routes::Login < Invidious::Routes::BaseRoute
|
|||||||
sid = Base64.urlsafe_encode(Random::Secure.random_bytes(32))
|
sid = Base64.urlsafe_encode(Random::Secure.random_bytes(32))
|
||||||
PG_DB.exec("INSERT INTO session_ids VALUES ($1, $2, $3)", sid, email, Time.utc)
|
PG_DB.exec("INSERT INTO session_ids VALUES ($1, $2, $3)", sid, email, Time.utc)
|
||||||
|
|
||||||
if Kemal.config.ssl || config.https_only
|
if Kemal.config.ssl || CONFIG.https_only
|
||||||
secure = true
|
secure = true
|
||||||
else
|
else
|
||||||
secure = false
|
secure = false
|
||||||
end
|
end
|
||||||
|
|
||||||
if config.domain
|
if CONFIG.domain
|
||||||
env.response.cookies["SID"] = HTTP::Cookie.new(name: "SID", domain: "#{config.domain}", value: sid, expires: Time.utc + 2.years,
|
env.response.cookies["SID"] = HTTP::Cookie.new(name: "SID", domain: "#{CONFIG.domain}", value: sid, expires: Time.utc + 2.years,
|
||||||
secure: secure, http_only: true)
|
secure: secure, http_only: true)
|
||||||
else
|
else
|
||||||
env.response.cookies["SID"] = HTTP::Cookie.new(name: "SID", value: sid, expires: Time.utc + 2.years,
|
env.response.cookies["SID"] = HTTP::Cookie.new(name: "SID", value: sid, expires: Time.utc + 2.years,
|
||||||
@ -354,7 +354,7 @@ class Invidious::Routes::Login < Invidious::Routes::BaseRoute
|
|||||||
env.response.cookies << cookie
|
env.response.cookies << cookie
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
if !config.registration_enabled
|
if !CONFIG.registration_enabled
|
||||||
return error_template(400, "Registration has been disabled by administrator.")
|
return error_template(400, "Registration has been disabled by administrator.")
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -369,7 +369,7 @@ class Invidious::Routes::Login < Invidious::Routes::BaseRoute
|
|||||||
|
|
||||||
password = password.byte_slice(0, 55)
|
password = password.byte_slice(0, 55)
|
||||||
|
|
||||||
if config.captcha_enabled
|
if CONFIG.captcha_enabled
|
||||||
captcha_type = env.params.body["captcha_type"]?
|
captcha_type = env.params.body["captcha_type"]?
|
||||||
answer = env.params.body["answer"]?
|
answer = env.params.body["answer"]?
|
||||||
change_type = env.params.body["change_type"]?
|
change_type = env.params.body["change_type"]?
|
||||||
@ -445,14 +445,14 @@ class Invidious::Routes::Login < Invidious::Routes::BaseRoute
|
|||||||
view_name = "subscriptions_#{sha256(user.email)}"
|
view_name = "subscriptions_#{sha256(user.email)}"
|
||||||
PG_DB.exec("CREATE MATERIALIZED VIEW #{view_name} AS #{MATERIALIZED_VIEW_SQL.call(user.email)}")
|
PG_DB.exec("CREATE MATERIALIZED VIEW #{view_name} AS #{MATERIALIZED_VIEW_SQL.call(user.email)}")
|
||||||
|
|
||||||
if Kemal.config.ssl || config.https_only
|
if Kemal.config.ssl || CONFIG.https_only
|
||||||
secure = true
|
secure = true
|
||||||
else
|
else
|
||||||
secure = false
|
secure = false
|
||||||
end
|
end
|
||||||
|
|
||||||
if config.domain
|
if CONFIG.domain
|
||||||
env.response.cookies["SID"] = HTTP::Cookie.new(name: "SID", domain: "#{config.domain}", value: sid, expires: Time.utc + 2.years,
|
env.response.cookies["SID"] = HTTP::Cookie.new(name: "SID", domain: "#{CONFIG.domain}", value: sid, expires: Time.utc + 2.years,
|
||||||
secure: secure, http_only: true)
|
secure: secure, http_only: true)
|
||||||
else
|
else
|
||||||
env.response.cookies["SID"] = HTTP::Cookie.new(name: "SID", value: sid, expires: Time.utc + 2.years,
|
env.response.cookies["SID"] = HTTP::Cookie.new(name: "SID", value: sid, expires: Time.utc + 2.years,
|
||||||
|
@ -146,8 +146,8 @@ class Invidious::Routes::UserPreferences < Invidious::Routes::BaseRoute
|
|||||||
user = user.as(User)
|
user = user.as(User)
|
||||||
PG_DB.exec("UPDATE users SET preferences = $1 WHERE email = $2", preferences, user.email)
|
PG_DB.exec("UPDATE users SET preferences = $1 WHERE email = $2", preferences, user.email)
|
||||||
|
|
||||||
if config.admins.includes? user.email
|
if CONFIG.admins.includes? user.email
|
||||||
config.default_user_preferences.default_home = env.params.body["admin_default_home"]?.try &.as(String) || config.default_user_preferences.default_home
|
CONFIG.default_user_preferences.default_home = env.params.body["admin_default_home"]?.try &.as(String) || CONFIG.default_user_preferences.default_home
|
||||||
|
|
||||||
admin_feed_menu = [] of String
|
admin_feed_menu = [] of String
|
||||||
4.times do |index|
|
4.times do |index|
|
||||||
@ -156,40 +156,39 @@ class Invidious::Routes::UserPreferences < Invidious::Routes::BaseRoute
|
|||||||
admin_feed_menu << option
|
admin_feed_menu << option
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
config.default_user_preferences.feed_menu = admin_feed_menu
|
CONFIG.default_user_preferences.feed_menu = admin_feed_menu
|
||||||
|
|
||||||
popular_enabled = env.params.body["popular_enabled"]?.try &.as(String)
|
popular_enabled = env.params.body["popular_enabled"]?.try &.as(String)
|
||||||
popular_enabled ||= "off"
|
popular_enabled ||= "off"
|
||||||
config.popular_enabled = popular_enabled == "on"
|
CONFIG.popular_enabled = popular_enabled == "on"
|
||||||
|
|
||||||
captcha_enabled = env.params.body["captcha_enabled"]?.try &.as(String)
|
captcha_enabled = env.params.body["captcha_enabled"]?.try &.as(String)
|
||||||
captcha_enabled ||= "off"
|
captcha_enabled ||= "off"
|
||||||
config.captcha_enabled = captcha_enabled == "on"
|
CONFIG.captcha_enabled = captcha_enabled == "on"
|
||||||
|
|
||||||
login_enabled = env.params.body["login_enabled"]?.try &.as(String)
|
login_enabled = env.params.body["login_enabled"]?.try &.as(String)
|
||||||
login_enabled ||= "off"
|
login_enabled ||= "off"
|
||||||
config.login_enabled = login_enabled == "on"
|
CONFIG.login_enabled = login_enabled == "on"
|
||||||
|
|
||||||
registration_enabled = env.params.body["registration_enabled"]?.try &.as(String)
|
registration_enabled = env.params.body["registration_enabled"]?.try &.as(String)
|
||||||
registration_enabled ||= "off"
|
registration_enabled ||= "off"
|
||||||
config.registration_enabled = registration_enabled == "on"
|
CONFIG.registration_enabled = registration_enabled == "on"
|
||||||
|
|
||||||
statistics_enabled = env.params.body["statistics_enabled"]?.try &.as(String)
|
statistics_enabled = env.params.body["statistics_enabled"]?.try &.as(String)
|
||||||
statistics_enabled ||= "off"
|
statistics_enabled ||= "off"
|
||||||
config.statistics_enabled = statistics_enabled == "on"
|
CONFIG.statistics_enabled = statistics_enabled == "on"
|
||||||
|
|
||||||
CONFIG.default_user_preferences = config.default_user_preferences
|
File.write("config/config.yml", CONFIG.to_yaml)
|
||||||
File.write("config/config.yml", config.to_yaml)
|
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
if Kemal.config.ssl || config.https_only
|
if Kemal.config.ssl || CONFIG.https_only
|
||||||
secure = true
|
secure = true
|
||||||
else
|
else
|
||||||
secure = false
|
secure = false
|
||||||
end
|
end
|
||||||
|
|
||||||
if config.domain
|
if CONFIG.domain
|
||||||
env.response.cookies["PREFS"] = HTTP::Cookie.new(name: "PREFS", domain: "#{config.domain}", value: preferences, expires: Time.utc + 2.years,
|
env.response.cookies["PREFS"] = HTTP::Cookie.new(name: "PREFS", domain: "#{CONFIG.domain}", value: preferences, expires: Time.utc + 2.years,
|
||||||
secure: secure, http_only: true)
|
secure: secure, http_only: true)
|
||||||
else
|
else
|
||||||
env.response.cookies["PREFS"] = HTTP::Cookie.new(name: "PREFS", value: preferences, expires: Time.utc + 2.years,
|
env.response.cookies["PREFS"] = HTTP::Cookie.new(name: "PREFS", value: preferences, expires: Time.utc + 2.years,
|
||||||
@ -234,14 +233,14 @@ class Invidious::Routes::UserPreferences < Invidious::Routes::BaseRoute
|
|||||||
|
|
||||||
preferences = preferences.to_json
|
preferences = preferences.to_json
|
||||||
|
|
||||||
if Kemal.config.ssl || config.https_only
|
if Kemal.config.ssl || CONFIG.https_only
|
||||||
secure = true
|
secure = true
|
||||||
else
|
else
|
||||||
secure = false
|
secure = false
|
||||||
end
|
end
|
||||||
|
|
||||||
if config.domain
|
if CONFIG.domain
|
||||||
env.response.cookies["PREFS"] = HTTP::Cookie.new(name: "PREFS", domain: "#{config.domain}", value: preferences, expires: Time.utc + 2.years,
|
env.response.cookies["PREFS"] = HTTP::Cookie.new(name: "PREFS", domain: "#{CONFIG.domain}", value: preferences, expires: Time.utc + 2.years,
|
||||||
secure: secure, http_only: true)
|
secure: secure, http_only: true)
|
||||||
else
|
else
|
||||||
env.response.cookies["PREFS"] = HTTP::Cookie.new(name: "PREFS", value: preferences, expires: Time.utc + 2.years,
|
env.response.cookies["PREFS"] = HTTP::Cookie.new(name: "PREFS", value: preferences, expires: Time.utc + 2.years,
|
||||||
|
@ -1,14 +1,14 @@
|
|||||||
module Invidious::Routing
|
module Invidious::Routing
|
||||||
macro get(path, controller, method = :handle)
|
macro get(path, controller, method = :handle)
|
||||||
get {{ path }} do |env|
|
get {{ path }} do |env|
|
||||||
controller_instance = {{ controller }}.new(config)
|
controller_instance = {{ controller }}.new
|
||||||
controller_instance.{{ method.id }}(env)
|
controller_instance.{{ method.id }}(env)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
macro post(path, controller, method = :handle)
|
macro post(path, controller, method = :handle)
|
||||||
post {{ path }} do |env|
|
post {{ path }} do |env|
|
||||||
controller_instance = {{ controller }}.new(config)
|
controller_instance = {{ controller }}.new
|
||||||
controller_instance.{{ method.id }}(env)
|
controller_instance.{{ method.id }}(env)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -208,14 +208,14 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<% # Web notifications are only supported over HTTPS %>
|
<% # Web notifications are only supported over HTTPS %>
|
||||||
<% if Kemal.config.ssl || config.https_only %>
|
<% if Kemal.config.ssl || CONFIG.https_only %>
|
||||||
<div class="pure-control-group">
|
<div class="pure-control-group">
|
||||||
<a href="#" data-onclick="notification_requestPermission"><%= translate(locale, "Enable web notifications") %></a>
|
<a href="#" data-onclick="notification_requestPermission"><%= translate(locale, "Enable web notifications") %></a>
|
||||||
</div>
|
</div>
|
||||||
<% end %>
|
<% end %>
|
||||||
<% end %>
|
<% end %>
|
||||||
|
|
||||||
<% if env.get?("user") && config.admins.includes? env.get?("user").as(User).email %>
|
<% if env.get?("user") && CONFIG.admins.includes? env.get?("user").as(User).email %>
|
||||||
<legend><%= translate(locale, "Administrator preferences") %></legend>
|
<legend><%= translate(locale, "Administrator preferences") %></legend>
|
||||||
|
|
||||||
<div class="pure-control-group">
|
<div class="pure-control-group">
|
||||||
@ -240,28 +240,28 @@
|
|||||||
|
|
||||||
<div class="pure-control-group">
|
<div class="pure-control-group">
|
||||||
<label for="popular_enabled"><%= translate(locale, "Popular enabled: ") %></label>
|
<label for="popular_enabled"><%= translate(locale, "Popular enabled: ") %></label>
|
||||||
<input name="popular_enabled" id="popular_enabled" type="checkbox" <% if config.popular_enabled %>checked<% end %>>
|
<input name="popular_enabled" id="popular_enabled" type="checkbox" <% if CONFIG.popular_enabled %>checked<% end %>>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
<div class="pure-control-group">
|
<div class="pure-control-group">
|
||||||
<label for="captcha_enabled"><%= translate(locale, "CAPTCHA enabled: ") %></label>
|
<label for="captcha_enabled"><%= translate(locale, "CAPTCHA enabled: ") %></label>
|
||||||
<input name="captcha_enabled" id="captcha_enabled" type="checkbox" <% if config.captcha_enabled %>checked<% end %>>
|
<input name="captcha_enabled" id="captcha_enabled" type="checkbox" <% if CONFIG.captcha_enabled %>checked<% end %>>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="pure-control-group">
|
<div class="pure-control-group">
|
||||||
<label for="login_enabled"><%= translate(locale, "Login enabled: ") %></label>
|
<label for="login_enabled"><%= translate(locale, "Login enabled: ") %></label>
|
||||||
<input name="login_enabled" id="login_enabled" type="checkbox" <% if config.login_enabled %>checked<% end %>>
|
<input name="login_enabled" id="login_enabled" type="checkbox" <% if CONFIG.login_enabled %>checked<% end %>>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="pure-control-group">
|
<div class="pure-control-group">
|
||||||
<label for="registration_enabled"><%= translate(locale, "Registration enabled: ") %></label>
|
<label for="registration_enabled"><%= translate(locale, "Registration enabled: ") %></label>
|
||||||
<input name="registration_enabled" id="registration_enabled" type="checkbox" <% if config.registration_enabled %>checked<% end %>>
|
<input name="registration_enabled" id="registration_enabled" type="checkbox" <% if CONFIG.registration_enabled %>checked<% end %>>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="pure-control-group">
|
<div class="pure-control-group">
|
||||||
<label for="statistics_enabled"><%= translate(locale, "Report statistics: ") %></label>
|
<label for="statistics_enabled"><%= translate(locale, "Report statistics: ") %></label>
|
||||||
<input name="statistics_enabled" id="statistics_enabled" type="checkbox" <% if config.statistics_enabled %>checked<% end %>>
|
<input name="statistics_enabled" id="statistics_enabled" type="checkbox" <% if CONFIG.statistics_enabled %>checked<% end %>>
|
||||||
</div>
|
</div>
|
||||||
<% end %>
|
<% end %>
|
||||||
|
|
||||||
|
@ -87,7 +87,7 @@
|
|||||||
<i class="icon ion-ios-cog"></i>
|
<i class="icon ion-ios-cog"></i>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
<% if config.login_enabled %>
|
<% if CONFIG.login_enabled %>
|
||||||
<div class="pure-u-1-3">
|
<div class="pure-u-1-3">
|
||||||
<a href="/login?referer=<%= env.get?("current_page") %>" class="pure-menu-heading">
|
<a href="/login?referer=<%= env.get?("current_page") %>" class="pure-menu-heading">
|
||||||
<%= translate(locale, "Log in") %>
|
<%= translate(locale, "Log in") %>
|
||||||
|
Loading…
Reference in New Issue
Block a user