#!/usr/bin/env perl use v5.34; use warnings; use Config (); use FindBin (); use File::Spec; use Getopt::Long qw(GetOptionsFromArray); BEGIN { require Cwd; require File::Spec; require lib; my @roots = grep { defined && -d $_ } map { Cwd::abs_path($_) } ( File::Spec->catdir($FindBin::RealBin, '..'), File::Spec->catdir($FindBin::RealBin, '..', '..'), ); my @inc; for my $root (@roots) { push @inc, File::Spec->catdir($root, 'lib'), File::Spec->catdir($root, 'local', 'lib', 'perl5'), File::Spec->catdir($root, 'local', 'lib', 'perl5', $Config::Config{archname}); } lib->import(@inc); } use ATProto::PDS::Config qw(load_config); use ATProto::PDS::Store::SQLite; use ATProto::PDS::Auth::Password qw(random_hex); use ATProto::PDS::Identity qw(service_did); use ATProto::PDS::Repo::Manager; my $root = File::Spec->rel2abs(File::Spec->catdir($FindBin::RealBin, '..')); my $config_path = $ENV{PERLSKY_CONFIG} || File::Spec->catfile($root, 'etc', 'perlsky.example.json'); my $config = load_config($config_path); my $command = shift(@ARGV) // q(); if ($command eq 'create-invite') { my $use_count = 1; my $for_account = service_did($config); my $created_by = service_did($config); GetOptionsFromArray( \@ARGV, 'use-count=i' => \$use_count, 'for-account=s' => \$for_account, 'created-by=s' => \$created_by, ) or die usage(); my $store = _store($config, $root); my $code = _new_invite_code(); $store->create_invite_code( code => $code, for_account => $for_account, created_by => $created_by, use_count => $use_count, ); print "$code\n"; exit 0; } if ($command eq 'repair-binary-columns') { my $store = _store($config, $root); my $result = $store->repair_binary_columns; for my $key (sort keys %$result) { print "$key=$result->{$key}\n"; } exit 0; } if ($command eq 'repair-invalid-tids') { my $did; my $force = 0; GetOptionsFromArray( \@ARGV, 'did=s' => \$did, 'force!' => \$force, ) or die usage(); my $store = _store($config, $root); my $manager = ATProto::PDS::Repo::Manager->new(store => $store); my @accounts = $did ? do { my $account = $store->get_account_by_did($did) or die "account not found for did=$did\n"; ($account); } : @{ $store->list_accounts }; for my $account (@accounts) { my $result = $manager->repair_invalid_tids($account, force => $force); my $label = $account->{handle} // $account->{did}; print join( q{ }, $label, 'changed=' . ($result->{changed} // 0), 'repaired_paths=' . ($result->{repaired_paths} // 0), 'rewritten_refs=' . ($result->{rewritten_refs} // 0), 'rev_repaired=' . ($result->{rev_repaired} // 0), (defined($result->{imported}{rev}) ? ('new_rev=' . $result->{imported}{rev}) : ()), ), "\n"; } exit 0; } die usage(); sub _store { my ($config, $root) = @_; return ATProto::PDS::Store::SQLite->new( path => $config->{db_path} || File::Spec->catfile($root, 'data', 'runtime', 'perlsky.sqlite'), )->bootstrap; } sub _new_invite_code { return 'perlsky-' . substr(random_hex(8), 0, 12); } sub usage { return <<'EOF'; usage: perlsky-admin create-invite [--use-count N] [--for-account DID] [--created-by DID] perlsky-admin repair-binary-columns perlsky-admin repair-invalid-tids [--did DID] [--force] EOF }