require 'json' require 'sinatra/base' require_relative 'init' require_relative 'authenticator' require_relative 'models/user' require_relative 'query_parser' class Server < Sinatra::Application register Sinatra::ActiveRecordExtension set :port, 3000 PAGE_LIMIT = 25 HOSTNAME = 'lycan.feeds.blue' helpers do def json_response(data) content_type :json JSON.generate(data) end def json_error(name, message, status: 400) content_type :json [status, JSON.generate({ error: name, message: message })] end end before do @authenticator = Authenticator.new(hostname: HOSTNAME) end get '/xrpc/blue.feeds.lycan.searchPosts' do headers['access-control-allow-origin'] = '*' if settings.development? user = User.find_by(did: params[:user]) return json_error('UserNotFound', 'Missing "user" parameter') if user.nil? else begin auth_header = env['HTTP_AUTHORIZATION'] did = @authenticator.decode_user_from_jwt(auth_header, 'blue.feeds.lycan.searchPosts') rescue StandardError => e p e end user = did && User.find_by(did: did) return json_error('UserNotFound', 'Missing authentication header') if user.nil? end if params[:query].to_s.strip.empty? return json_error('MissingParameter', 'Missing "query" parameter') end if params[:collection].to_s.strip.empty? return json_error('MissingParameter', 'Missing "collection" parameter') end terms = QueryParser.new(params[:query]).terms collection = case params[:collection] when 'likes' then user.likes when 'pins' then user.pins when 'quotes' then user.quotes when 'reposts' then user.reposts else return json_error('InvalidParameter', 'Invalid search collection') end items = collection .joins(:post) .includes(:post => :user) .matching_terms(terms) .reverse_chronologically .after_cursor(params[:cursor]) .limit(PAGE_LIMIT) post_uris = case params[:collection] when 'quotes' items.map { |x| "at://#{user.did}/app.bsky.feed.post/#{x.rkey}" } else items.map(&:post).map(&:at_uri) end json_response(terms: terms, posts: post_uris, cursor: items.last&.cursor) end get '/.well-known/did.json' do json_response({ '@context': ['https://www.w3.org/ns/did/v1'], id: "did:web:#{HOSTNAME}", service: [ { id: '#lycan', type: 'LycanServer', serviceEndpoint: "https://#{HOSTNAME}" } ] }) end end