get(SettingsInterface::class); assert($settingsService instanceof SettingsInterface); /** @var array{accountDb:string,sequencerDb:string,didCacheDb:string,actorStoreDir:string,blobstoreDir:string}|null $settings */ $settings = $settingsService->get('database'); if (!is_array($settings)) { throw new \RuntimeException('Database settings are missing.'); } return $settings; }; /** * @param ContainerInterface $c * @param string $key * @return Database */ $getDb = static function (ContainerInterface $c, string $key): Database { $db = $c->get($key); assert($db instanceof Database); return $db; }; return function (ContainerBuilder $containerBuilder) use ($dbSettings, $getDb) { $containerBuilder->addDefinitions([ // Database singletons 'db.account' => function (ContainerInterface $c) use ($dbSettings): Database { $db = new Database($dbSettings($c)['accountDb']); AccountSchema::apply($db); return $db; }, 'db.sequencer' => function (ContainerInterface $c) use ($dbSettings): Database { $db = new Database($dbSettings($c)['sequencerDb']); SequencerSchema::apply($db); return $db; }, 'db.didCache' => function (ContainerInterface $c) use ($dbSettings): Database { $db = new Database($dbSettings($c)['didCacheDb']); DidCacheSchema::apply($db); return $db; }, // Clients and services AppViewClient::class => function (ContainerInterface $container): AppViewClient { $settings = $container->get(SettingsInterface::class); assert($settings instanceof SettingsInterface); /** @var array{bskyAppViewUrl?: string} $pdsSettings */ $pdsSettings = $settings->get('pds'); $baseUrl = $pdsSettings['bskyAppViewUrl'] ?? throw new \RuntimeException('PDS_BSKY_APP_VIEW_URL is required to use the AppView client.'); $httpClient = new GuzzleClient([ 'base_uri' => rtrim($baseUrl, '/') . '/', 'timeout' => 10.0, 'headers' => ['Accept' => 'application/json'], ]); return new HttpAppViewClient($httpClient); }, AccountRepository::class => fn (ContainerInterface $c): SqliteAccountRepository => new SqliteAccountRepository($getDb($c, 'db.account')), AccountAuthenticator::class => autowire(RepositoryAccountAuthenticator::class), AccountCreator::class => function (ContainerInterface $c): AccountCreator { $settings = $c->get(SettingsInterface::class); assert($settings instanceof SettingsInterface); /** @var array{hostname: string} $pdsSettings */ $pdsSettings = $settings->get('pds'); $rotationKey = $c->get(Keypair::class); assert($rotationKey instanceof Keypair); $accounts = $c->get(AccountRepository::class); assert($accounts instanceof AccountRepository); $actors = $c->get(ActorRepository::class); assert($actors instanceof ActorRepository); $handleValidator = $c->get(HandleValidator::class); assert($handleValidator instanceof HandleValidator); $inviteCodes = $c->get(InviteCodeRepository::class); assert($inviteCodes instanceof InviteCodeRepository); $actorStores = $c->get(ActorStoreFactory::class); assert($actorStores instanceof ActorStoreFactory); $keypairs = $c->get(KeypairFactory::class); assert($keypairs instanceof KeypairFactory); $passwords = $c->get(PasswordHasher::class); assert($passwords instanceof PasswordHasher); $plc = $c->get(PlcDirectoryClient::class); assert($plc instanceof PlcDirectoryClient); $didResolver = $c->get(DidResolver::class); assert($didResolver instanceof DidResolver); $repoInit = $c->get(RepoInitializer::class); assert($repoInit instanceof RepoInitializer); $sequencer = $c->get(SequencerRepository::class); assert($sequencer instanceof SequencerRepository); $events = $c->get(SubscribeReposEventFactory::class); assert($events instanceof SubscribeReposEventFactory); $tokens = $c->get(AuthTokenIssuer::class); assert($tokens instanceof AuthTokenIssuer); $refreshTokens = $c->get(RefreshTokenRepository::class); assert($refreshTokens instanceof RefreshTokenRepository); return new AccountCreator( accounts: $accounts, actors: $actors, handleValidator: $handleValidator, inviteCodes: $inviteCodes, actorStores: $actorStores, keypairs: $keypairs, passwordHasher: $passwords, plc: $plc, didResolver: $didResolver, repoInitializer: $repoInit, sequencer: $sequencer, events: $events, tokens: $tokens, refreshTokens: $refreshTokens, plcRotationKey: $rotationKey, inviteRequired: true, hostname: $pdsSettings['hostname'], ); }, AccountDeviceRepository::class => fn (ContainerInterface $c): SqliteAccountDeviceRepository => new SqliteAccountDeviceRepository($getDb($c, 'db.account')), ActorRepository::class => fn (ContainerInterface $c): SqliteActorRepository => new SqliteActorRepository($getDb($c, 'db.account')), ActorStoreFactory::class => function (ContainerInterface $c) use ($dbSettings): SqliteActorStoreFactory { $settings = $dbSettings($c); return new SqliteActorStoreFactory( actorsDirectory: $settings['actorStoreDir'], blobsDirectory: $settings['blobstoreDir'], ); }, AuthorizationRequestRepository::class => fn (ContainerInterface $c): SqliteAuthorizationRequestRepository => new SqliteAuthorizationRequestRepository($getDb($c, 'db.account')), AuthorizedClientRepository::class => fn (ContainerInterface $c): SqliteAuthorizedClientRepository => new SqliteAuthorizedClientRepository($getDb($c, 'db.account')), CarReader::class => autowire(NativeCarReader::class), CarWriter::class => autowire(NativeCarWriter::class), DagCborDecoder::class => autowire(NativeDagCborDecoder::class), DagCborEncoder::class => autowire(NativeDagCborEncoder::class), DeviceRepository::class => fn (ContainerInterface $c): SqliteDeviceRepository => new SqliteDeviceRepository($getDb($c, 'db.account')), DidCacheRepository::class => fn (ContainerInterface $c): SqliteDidCacheRepository => new SqliteDidCacheRepository($getDb($c, 'db.didCache')), DidResolver::class => function (ContainerInterface $c): HttpDidResolver { $settings = $c->get(SettingsInterface::class); assert($settings instanceof SettingsInterface); /** @var array{plcDirectoryUrl: string} $pdsSettings */ $pdsSettings = $settings->get('pds'); $httpClient = new GuzzleClient(['timeout' => 10.0]); $cache = $c->get(DidCacheRepository::class); assert($cache instanceof SqliteDidCacheRepository); return new HttpDidResolver($httpClient, $cache, $pdsSettings['plcDirectoryUrl']); }, AppPasswordRepository::class => fn (ContainerInterface $c): SqliteAppPasswordRepository => new SqliteAppPasswordRepository($getDb($c, 'db.account')), AuthTokenIssuer::class => function (ContainerInterface $c): JwtAuthTokenIssuer { $settings = $c->get(SettingsInterface::class); assert($settings instanceof SettingsInterface); /** @var array{hostname: string, jwtSecret: string} $pdsSettings */ $pdsSettings = $settings->get('pds'); return new JwtAuthTokenIssuer( secret: $pdsSettings['jwtSecret'], issuer: $pdsSettings['hostname'], ); }, EmailTokenRepository::class => fn (ContainerInterface $c): SqliteEmailTokenRepository => new SqliteEmailTokenRepository($getDb($c, 'db.account')), HandleValidator::class => function (ContainerInterface $c): HandleValidator { $settings = $c->get(SettingsInterface::class); assert($settings instanceof SettingsInterface); /** @var array{hostname: string} $pdsSettings */ $pdsSettings = $settings->get('pds'); $actors = $c->get(ActorRepository::class); assert($actors instanceof ActorRepository); return new HandleValidator($actors, [".{$pdsSettings['hostname']}"]); }, InviteCodeRepository::class => fn (ContainerInterface $c): SqliteInviteCodeRepository => new SqliteInviteCodeRepository($getDb($c, 'db.account')), InviteCodeGenerator::class => function (ContainerInterface $c): InviteCodeGenerator { $settings = $c->get(SettingsInterface::class); assert($settings instanceof SettingsInterface); /** @var array{hostname: string} $pdsSettings */ $pdsSettings = $settings->get('pds'); return new InviteCodeGenerator($pdsSettings['hostname']); }, Keypair::class => function (ContainerInterface $c): Keypair { $settings = $c->get(SettingsInterface::class); assert($settings instanceof SettingsInterface); /** @var array{plcRotationKeyHex: string} $pdsSettings */ $pdsSettings = $settings->get('pds'); $factory = $c->get(KeypairFactory::class); assert($factory instanceof KeypairFactory); return $factory->fromPrivateKeyHex($pdsSettings['plcRotationKeyHex']); }, KeypairFactory::class => autowire(Secp256k1KeypairFactory::class), LexiconRepository::class => fn (ContainerInterface $c): SqliteLexiconRepository => new SqliteLexiconRepository($getDb($c, 'db.account')), OAuthTokenRepository::class => fn (ContainerInterface $c): SqliteOAuthTokenRepository => new SqliteOAuthTokenRepository($getDb($c, 'db.account')), PlcDirectoryClient::class => function (ContainerInterface $c): HttpPlcDirectoryClient { $settings = $c->get(SettingsInterface::class); assert($settings instanceof SettingsInterface); /** @var array{plcDirectoryUrl: string} $pdsSettings */ $pdsSettings = $settings->get('pds'); $http = new GuzzleClient(['timeout' => 15.0]); $cbor = $c->get(DagCborEncoder::class); assert($cbor instanceof DagCborEncoder); return new HttpPlcDirectoryClient($http, $cbor, $pdsSettings['plcDirectoryUrl']); }, PasswordHasher::class => autowire(ScryptPasswordHasher::class), RefreshTokenRepository::class => fn (ContainerInterface $c): SqliteRefreshTokenRepository => new SqliteRefreshTokenRepository($getDb($c, 'db.account')), RepoInitializer::class => autowire(RepoInitializer::class), RepoRootRepository::class => fn (ContainerInterface $c): SqliteRepoRootRepository => new SqliteRepoRootRepository($getDb($c, 'db.account')), SequencerRepository::class => fn (ContainerInterface $c): SqliteSequencerRepository => new SqliteSequencerRepository($getDb($c, 'db.sequencer')), SubscribeReposEventFactory::class => autowire(SubscribeReposEventFactory::class), UsedRefreshTokenRepository::class => fn (ContainerInterface $c): SqliteUsedRefreshTokenRepository => new SqliteUsedRefreshTokenRepository($getDb($c, 'db.account')), ]); };