# Copyright © 2025-26 l5yth & contributors # # 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/worker_pool" 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 :federation_worker_pool, 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_enabled? ensure_federation_worker_pool! else shutdown_federation_worker_pool! end 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