Files
potato-mesh/web/lib/potato_mesh/application.rb
2025-10-18 13:01:48 +02:00

197 lines
5.9 KiB
Ruby

# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# frozen_string_literal: true
require "sinatra/base"
require "json"
require "sqlite3"
require "fileutils"
require "logger"
require "rack/utils"
require "open3"
require "resolv"
require "socket"
require "time"
require "openssl"
require "base64"
require "prometheus/client"
require "prometheus/client/formats/text"
require "prometheus/middleware/collector"
require "prometheus/middleware/exporter"
require "net/http"
require "uri"
require "ipaddr"
require "set"
require "digest"
require_relative "config"
require_relative "sanitizer"
require_relative "meta"
require_relative "logging"
require_relative "application/helpers"
require_relative "application/errors"
require_relative "application/database"
require_relative "application/networking"
require_relative "application/identity"
require_relative "application/federation"
require_relative "application/prometheus"
require_relative "application/queries"
require_relative "application/data_processing"
require_relative "application/filesystem"
require_relative "application/instances"
require_relative "application/routes/api"
require_relative "application/routes/ingest"
require_relative "application/routes/root"
module PotatoMesh
class Application < Sinatra::Base
extend App::Helpers
extend App::Database
extend App::Networking
extend App::Identity
extend App::Federation
extend App::Instances
extend App::Prometheus
extend App::Queries
extend App::DataProcessing
extend App::Filesystem
helpers App::Helpers
include App::Database
include App::Networking
include App::Identity
include App::Federation
include App::Instances
include App::Prometheus
include App::Queries
include App::DataProcessing
include App::Filesystem
register App::Routes::Api
register App::Routes::Ingest
register App::Routes::Root
DEFAULT_PORT = 41_447
DEFAULT_BIND_ADDRESS = "0.0.0.0"
APP_VERSION = determine_app_version
INSTANCE_PRIVATE_KEY, INSTANCE_KEY_GENERATED = load_or_generate_instance_private_key
INSTANCE_PUBLIC_KEY_PEM = INSTANCE_PRIVATE_KEY.public_key.export
SELF_INSTANCE_ID = Digest::SHA256.hexdigest(INSTANCE_PUBLIC_KEY_PEM)
INSTANCE_DOMAIN, INSTANCE_DOMAIN_SOURCE = determine_instance_domain
# Adjust the runtime logger severity to match the DEBUG flag.
#
# @return [void]
def self.apply_logger_level!
logger = settings.logger
return unless logger
logger.level = PotatoMesh::Config.debug? ? Logger::DEBUG : Logger::WARN
end
# Determine the port the application should listen on by honouring the
# conventional +PORT+ environment variable used by hosting platforms. Any
# non-numeric or out-of-range values fall back to the provided default to
# keep the application bootable in misconfigured environments.
#
# @param default_port [Integer] fallback port when +ENV['PORT']+ is absent or invalid.
# @return [Integer] port number for the HTTP server.
def self.resolve_port(default_port: DEFAULT_PORT)
raw_port = ENV["PORT"]
return default_port if raw_port.nil?
trimmed = raw_port.to_s.strip
return default_port if trimmed.empty?
begin
port = Integer(trimmed, 10)
rescue ArgumentError
return default_port
end
return default_port unless port.positive?
return default_port unless PotatoMesh::Sanitizer.valid_port?(trimmed)
port
end
configure do
set :public_folder, File.expand_path("../../public", __dir__)
set :views, File.expand_path("../../views", __dir__)
set :federation_thread, nil
set :port, resolve_port
set :bind, DEFAULT_BIND_ADDRESS
app_logger = PotatoMesh::Logging.build_logger($stdout)
set :logger, app_logger
use Rack::CommonLogger, app_logger
use Rack::Deflater
use ::Prometheus::Middleware::Collector
use ::Prometheus::Middleware::Exporter
apply_logger_level!
perform_initial_filesystem_setup!
cleanup_legacy_well_known_artifacts
init_db unless db_schema_present?
ensure_schema_upgrades
log_instance_domain_resolution
log_instance_public_key
refresh_well_known_document_if_stale
ensure_self_instance_record!
update_all_prometheus_metrics_from_nodes
if federation_announcements_active?
start_initial_federation_announcement!
start_federation_announcer!
elsif federation_enabled?
debug_log(
"Federation announcements disabled",
context: "federation",
reason: "test environment",
)
else
debug_log(
"Federation announcements disabled",
context: "federation",
reason: "configuration",
)
end
end
end
end
if defined?(Sinatra::Application) && Sinatra::Application != PotatoMesh::Application
Sinatra.send(:remove_const, :Application)
end
Sinatra::Application = PotatoMesh::Application unless defined?(Sinatra::Application)
APP_VERSION = PotatoMesh::Application::APP_VERSION unless defined?(APP_VERSION)
SELF_INSTANCE_ID = PotatoMesh::Application::SELF_INSTANCE_ID unless defined?(SELF_INSTANCE_ID)
[
PotatoMesh::App::Helpers,
PotatoMesh::App::Database,
PotatoMesh::App::Networking,
PotatoMesh::App::Identity,
PotatoMesh::App::Federation,
PotatoMesh::App::Instances,
PotatoMesh::App::Prometheus,
PotatoMesh::App::Queries,
PotatoMesh::App::DataProcessing,
].each do |mod|
Object.include(mod) unless Object < mod
end