service.go raw

   1  package main
   2  
   3  import (
   4  	"context"
   5  	"errors"
   6  	"time"
   7  
   8  	"google.golang.org/grpc/codes"
   9  	"google.golang.org/grpc/status"
  10  	"next.orly.dev/pkg/lol/log"
  11  
  12  	"next.orly.dev/pkg/acl"
  13  	"next.orly.dev/pkg/database"
  14  	orlyaclv1 "next.orly.dev/pkg/proto/orlyacl/v1"
  15  	orlydbv1 "next.orly.dev/pkg/proto/orlydb/v1"
  16  )
  17  
  18  // ACLService implements the orlyaclv1.ACLServiceServer interface.
  19  type ACLService struct {
  20  	orlyaclv1.UnimplementedACLServiceServer
  21  	cfg   *Config
  22  	db    database.Database
  23  	ready bool
  24  }
  25  
  26  // NewACLService creates a new ACL service.
  27  func NewACLService(cfg *Config, db database.Database) *ACLService {
  28  	return &ACLService{
  29  		cfg:   cfg,
  30  		db:    db,
  31  		ready: false, // Not ready until Configure completes
  32  	}
  33  }
  34  
  35  // SetReady marks the service as ready (or not ready).
  36  func (s *ACLService) SetReady(ready bool) {
  37  	s.ready = ready
  38  	if ready {
  39  		log.I.F("ACL service is now ready")
  40  	}
  41  }
  42  
  43  // === Core ACL Methods ===
  44  
  45  func (s *ACLService) GetAccessLevel(ctx context.Context, req *orlyaclv1.AccessLevelRequest) (*orlyaclv1.AccessLevelResponse, error) {
  46  	level := acl.Registry.GetAccessLevel(req.Pubkey, req.Address)
  47  	return &orlyaclv1.AccessLevelResponse{Level: level}, nil
  48  }
  49  
  50  func (s *ACLService) CheckPolicy(ctx context.Context, req *orlyaclv1.PolicyCheckRequest) (*orlyaclv1.PolicyCheckResponse, error) {
  51  	ev := orlydbv1.ProtoToEvent(req.Event)
  52  	allowed, err := acl.Registry.CheckPolicy(ev)
  53  	resp := &orlyaclv1.PolicyCheckResponse{Allowed: allowed}
  54  	if err != nil {
  55  		resp.Error = err.Error()
  56  	}
  57  	return resp, nil
  58  }
  59  
  60  func (s *ACLService) GetACLInfo(ctx context.Context, req *orlyaclv1.Empty) (*orlyaclv1.ACLInfoResponse, error) {
  61  	name, description, documentation := acl.Registry.GetACLInfo()
  62  	return &orlyaclv1.ACLInfoResponse{
  63  		Name:          name,
  64  		Description:   description,
  65  		Documentation: documentation,
  66  	}, nil
  67  }
  68  
  69  func (s *ACLService) GetMode(ctx context.Context, req *orlyaclv1.Empty) (*orlyaclv1.ModeResponse, error) {
  70  	return &orlyaclv1.ModeResponse{Mode: acl.Registry.Type()}, nil
  71  }
  72  
  73  func (s *ACLService) Ready(ctx context.Context, req *orlyaclv1.Empty) (*orlyaclv1.ReadyResponse, error) {
  74  	// Check if service is fully configured and ready
  75  	return &orlyaclv1.ReadyResponse{Ready: s.ready}, nil
  76  }
  77  
  78  // === Follows ACL Methods ===
  79  
  80  func (s *ACLService) GetThrottleDelay(ctx context.Context, req *orlyaclv1.ThrottleDelayRequest) (*orlyaclv1.ThrottleDelayResponse, error) {
  81  	// Get the active ACL and check if it's Follows
  82  	for _, i := range acl.Registry.ACLs() {
  83  		if i.Type() == "follows" {
  84  			if follows, ok := i.(*acl.Follows); ok {
  85  				delay := follows.GetThrottleDelay(req.Pubkey, req.Ip)
  86  				return &orlyaclv1.ThrottleDelayResponse{DelayMs: delay.Milliseconds()}, nil
  87  			}
  88  		}
  89  	}
  90  	return &orlyaclv1.ThrottleDelayResponse{DelayMs: 0}, nil
  91  }
  92  
  93  func (s *ACLService) AddFollow(ctx context.Context, req *orlyaclv1.AddFollowRequest) (*orlyaclv1.Empty, error) {
  94  	acl.Registry.AddFollow(req.Pubkey)
  95  	return &orlyaclv1.Empty{}, nil
  96  }
  97  
  98  func (s *ACLService) GetFollowedPubkeys(ctx context.Context, req *orlyaclv1.Empty) (*orlyaclv1.FollowedPubkeysResponse, error) {
  99  	for _, i := range acl.Registry.ACLs() {
 100  		if i.Type() == "follows" {
 101  			if follows, ok := i.(*acl.Follows); ok {
 102  				pubkeys := follows.GetFollowedPubkeys()
 103  				return &orlyaclv1.FollowedPubkeysResponse{Pubkeys: pubkeys}, nil
 104  			}
 105  		}
 106  	}
 107  	return &orlyaclv1.FollowedPubkeysResponse{}, nil
 108  }
 109  
 110  func (s *ACLService) GetAdminRelays(ctx context.Context, req *orlyaclv1.Empty) (*orlyaclv1.AdminRelaysResponse, error) {
 111  	for _, i := range acl.Registry.ACLs() {
 112  		if i.Type() == "follows" {
 113  			if follows, ok := i.(*acl.Follows); ok {
 114  				urls := follows.AdminRelays()
 115  				return &orlyaclv1.AdminRelaysResponse{Urls: urls}, nil
 116  			}
 117  		}
 118  	}
 119  	return &orlyaclv1.AdminRelaysResponse{}, nil
 120  }
 121  
 122  // === Managed ACL Methods ===
 123  
 124  func (s *ACLService) BanPubkey(ctx context.Context, req *orlyaclv1.BanPubkeyRequest) (*orlyaclv1.Empty, error) {
 125  	managed := s.getManagedACL()
 126  	if managed == nil {
 127  		return nil, status.Errorf(codes.FailedPrecondition, "managed ACL not available")
 128  	}
 129  	managedACL := managed.GetManagedACL()
 130  	if managedACL == nil {
 131  		return nil, status.Errorf(codes.FailedPrecondition, "managed ACL database not available")
 132  	}
 133  	if err := managedACL.SaveBannedPubkey(req.Pubkey, req.Reason); err != nil {
 134  		return nil, status.Errorf(codes.Internal, "failed to ban pubkey: %v", err)
 135  	}
 136  	return &orlyaclv1.Empty{}, nil
 137  }
 138  
 139  func (s *ACLService) UnbanPubkey(ctx context.Context, req *orlyaclv1.PubkeyRequest) (*orlyaclv1.Empty, error) {
 140  	managed := s.getManagedACL()
 141  	if managed == nil {
 142  		return nil, status.Errorf(codes.FailedPrecondition, "managed ACL not available")
 143  	}
 144  	managedACL := managed.GetManagedACL()
 145  	if managedACL == nil {
 146  		return nil, status.Errorf(codes.FailedPrecondition, "managed ACL database not available")
 147  	}
 148  	if err := managedACL.RemoveBannedPubkey(req.Pubkey); err != nil {
 149  		return nil, status.Errorf(codes.Internal, "failed to unban pubkey: %v", err)
 150  	}
 151  	return &orlyaclv1.Empty{}, nil
 152  }
 153  
 154  func (s *ACLService) ListBannedPubkeys(ctx context.Context, req *orlyaclv1.Empty) (*orlyaclv1.ListBannedPubkeysResponse, error) {
 155  	managed := s.getManagedACL()
 156  	if managed == nil {
 157  		return nil, status.Errorf(codes.FailedPrecondition, "managed ACL not available")
 158  	}
 159  	managedACL := managed.GetManagedACL()
 160  	if managedACL == nil {
 161  		return nil, status.Errorf(codes.FailedPrecondition, "managed ACL database not available")
 162  	}
 163  	banned, err := managedACL.ListBannedPubkeys()
 164  	if err != nil {
 165  		return nil, status.Errorf(codes.Internal, "failed to list banned pubkeys: %v", err)
 166  	}
 167  	resp := &orlyaclv1.ListBannedPubkeysResponse{}
 168  	for _, b := range banned {
 169  		resp.Pubkeys = append(resp.Pubkeys, &orlyaclv1.BannedPubkey{
 170  			Pubkey: b.Pubkey,
 171  			Reason: b.Reason,
 172  			Added:  b.Added.Unix(),
 173  		})
 174  	}
 175  	return resp, nil
 176  }
 177  
 178  func (s *ACLService) AllowPubkey(ctx context.Context, req *orlyaclv1.AllowPubkeyRequest) (*orlyaclv1.Empty, error) {
 179  	managed := s.getManagedACL()
 180  	if managed == nil {
 181  		return nil, status.Errorf(codes.FailedPrecondition, "managed ACL not available")
 182  	}
 183  	managedACL := managed.GetManagedACL()
 184  	if managedACL == nil {
 185  		return nil, status.Errorf(codes.FailedPrecondition, "managed ACL database not available")
 186  	}
 187  	if err := managedACL.SaveAllowedPubkey(req.Pubkey, req.Reason); err != nil {
 188  		return nil, status.Errorf(codes.Internal, "failed to allow pubkey: %v", err)
 189  	}
 190  	return &orlyaclv1.Empty{}, nil
 191  }
 192  
 193  func (s *ACLService) DisallowPubkey(ctx context.Context, req *orlyaclv1.PubkeyRequest) (*orlyaclv1.Empty, error) {
 194  	managed := s.getManagedACL()
 195  	if managed == nil {
 196  		return nil, status.Errorf(codes.FailedPrecondition, "managed ACL not available")
 197  	}
 198  	managedACL := managed.GetManagedACL()
 199  	if managedACL == nil {
 200  		return nil, status.Errorf(codes.FailedPrecondition, "managed ACL database not available")
 201  	}
 202  	if err := managedACL.RemoveAllowedPubkey(req.Pubkey); err != nil {
 203  		return nil, status.Errorf(codes.Internal, "failed to disallow pubkey: %v", err)
 204  	}
 205  	return &orlyaclv1.Empty{}, nil
 206  }
 207  
 208  func (s *ACLService) ListAllowedPubkeys(ctx context.Context, req *orlyaclv1.Empty) (*orlyaclv1.ListAllowedPubkeysResponse, error) {
 209  	managed := s.getManagedACL()
 210  	if managed == nil {
 211  		return nil, status.Errorf(codes.FailedPrecondition, "managed ACL not available")
 212  	}
 213  	managedACL := managed.GetManagedACL()
 214  	if managedACL == nil {
 215  		return nil, status.Errorf(codes.FailedPrecondition, "managed ACL database not available")
 216  	}
 217  	allowed, err := managedACL.ListAllowedPubkeys()
 218  	if err != nil {
 219  		return nil, status.Errorf(codes.Internal, "failed to list allowed pubkeys: %v", err)
 220  	}
 221  	resp := &orlyaclv1.ListAllowedPubkeysResponse{}
 222  	for _, a := range allowed {
 223  		resp.Pubkeys = append(resp.Pubkeys, &orlyaclv1.AllowedPubkey{
 224  			Pubkey: a.Pubkey,
 225  			Reason: a.Reason,
 226  			Added:  a.Added.Unix(),
 227  		})
 228  	}
 229  	return resp, nil
 230  }
 231  
 232  func (s *ACLService) BanEvent(ctx context.Context, req *orlyaclv1.BanEventRequest) (*orlyaclv1.Empty, error) {
 233  	managed := s.getManagedACL()
 234  	if managed == nil {
 235  		return nil, status.Errorf(codes.FailedPrecondition, "managed ACL not available")
 236  	}
 237  	managedACL := managed.GetManagedACL()
 238  	if managedACL == nil {
 239  		return nil, status.Errorf(codes.FailedPrecondition, "managed ACL database not available")
 240  	}
 241  	if err := managedACL.SaveBannedEvent(req.EventId, req.Reason); err != nil {
 242  		return nil, status.Errorf(codes.Internal, "failed to ban event: %v", err)
 243  	}
 244  	return &orlyaclv1.Empty{}, nil
 245  }
 246  
 247  func (s *ACLService) UnbanEvent(ctx context.Context, req *orlyaclv1.EventRequest) (*orlyaclv1.Empty, error) {
 248  	managed := s.getManagedACL()
 249  	if managed == nil {
 250  		return nil, status.Errorf(codes.FailedPrecondition, "managed ACL not available")
 251  	}
 252  	managedACL := managed.GetManagedACL()
 253  	if managedACL == nil {
 254  		return nil, status.Errorf(codes.FailedPrecondition, "managed ACL database not available")
 255  	}
 256  	if err := managedACL.RemoveBannedEvent(req.EventId); err != nil {
 257  		return nil, status.Errorf(codes.Internal, "failed to unban event: %v", err)
 258  	}
 259  	return &orlyaclv1.Empty{}, nil
 260  }
 261  
 262  func (s *ACLService) ListBannedEvents(ctx context.Context, req *orlyaclv1.Empty) (*orlyaclv1.ListBannedEventsResponse, error) {
 263  	managed := s.getManagedACL()
 264  	if managed == nil {
 265  		return nil, status.Errorf(codes.FailedPrecondition, "managed ACL not available")
 266  	}
 267  	managedACL := managed.GetManagedACL()
 268  	if managedACL == nil {
 269  		return nil, status.Errorf(codes.FailedPrecondition, "managed ACL database not available")
 270  	}
 271  	banned, err := managedACL.ListBannedEvents()
 272  	if err != nil {
 273  		return nil, status.Errorf(codes.Internal, "failed to list banned events: %v", err)
 274  	}
 275  	resp := &orlyaclv1.ListBannedEventsResponse{}
 276  	for _, b := range banned {
 277  		resp.Events = append(resp.Events, &orlyaclv1.BannedEvent{
 278  			EventId: b.ID,
 279  			Reason:  b.Reason,
 280  			Added:   b.Added.Unix(),
 281  		})
 282  	}
 283  	return resp, nil
 284  }
 285  
 286  func (s *ACLService) AllowEvent(ctx context.Context, req *orlyaclv1.BanEventRequest) (*orlyaclv1.Empty, error) {
 287  	managed := s.getManagedACL()
 288  	if managed == nil {
 289  		return nil, status.Errorf(codes.FailedPrecondition, "managed ACL not available")
 290  	}
 291  	managedACL := managed.GetManagedACL()
 292  	if managedACL == nil {
 293  		return nil, status.Errorf(codes.FailedPrecondition, "managed ACL database not available")
 294  	}
 295  	if err := managedACL.SaveAllowedEvent(req.EventId, req.Reason); err != nil {
 296  		return nil, status.Errorf(codes.Internal, "failed to allow event: %v", err)
 297  	}
 298  	return &orlyaclv1.Empty{}, nil
 299  }
 300  
 301  func (s *ACLService) DisallowEvent(ctx context.Context, req *orlyaclv1.EventRequest) (*orlyaclv1.Empty, error) {
 302  	managed := s.getManagedACL()
 303  	if managed == nil {
 304  		return nil, status.Errorf(codes.FailedPrecondition, "managed ACL not available")
 305  	}
 306  	managedACL := managed.GetManagedACL()
 307  	if managedACL == nil {
 308  		return nil, status.Errorf(codes.FailedPrecondition, "managed ACL database not available")
 309  	}
 310  	if err := managedACL.RemoveAllowedEvent(req.EventId); err != nil {
 311  		return nil, status.Errorf(codes.Internal, "failed to disallow event: %v", err)
 312  	}
 313  	return &orlyaclv1.Empty{}, nil
 314  }
 315  
 316  func (s *ACLService) ListAllowedEvents(ctx context.Context, req *orlyaclv1.Empty) (*orlyaclv1.ListAllowedEventsResponse, error) {
 317  	managed := s.getManagedACL()
 318  	if managed == nil {
 319  		return nil, status.Errorf(codes.FailedPrecondition, "managed ACL not available")
 320  	}
 321  	managedACL := managed.GetManagedACL()
 322  	if managedACL == nil {
 323  		return nil, status.Errorf(codes.FailedPrecondition, "managed ACL database not available")
 324  	}
 325  	allowed, err := managedACL.ListAllowedEvents()
 326  	if err != nil {
 327  		return nil, status.Errorf(codes.Internal, "failed to list allowed events: %v", err)
 328  	}
 329  	resp := &orlyaclv1.ListAllowedEventsResponse{}
 330  	for _, a := range allowed {
 331  		resp.Events = append(resp.Events, &orlyaclv1.AllowedEvent{
 332  			EventId: a.ID,
 333  			Reason:  a.Reason,
 334  			Added:   a.Added.Unix(),
 335  		})
 336  	}
 337  	return resp, nil
 338  }
 339  
 340  func (s *ACLService) BlockIP(ctx context.Context, req *orlyaclv1.BlockIPRequest) (*orlyaclv1.Empty, error) {
 341  	managed := s.getManagedACL()
 342  	if managed == nil {
 343  		return nil, status.Errorf(codes.FailedPrecondition, "managed ACL not available")
 344  	}
 345  	managedACL := managed.GetManagedACL()
 346  	if managedACL == nil {
 347  		return nil, status.Errorf(codes.FailedPrecondition, "managed ACL database not available")
 348  	}
 349  	if err := managedACL.SaveBlockedIP(req.Ip, req.Reason); err != nil {
 350  		return nil, status.Errorf(codes.Internal, "failed to block IP: %v", err)
 351  	}
 352  	return &orlyaclv1.Empty{}, nil
 353  }
 354  
 355  func (s *ACLService) UnblockIP(ctx context.Context, req *orlyaclv1.IPRequest) (*orlyaclv1.Empty, error) {
 356  	managed := s.getManagedACL()
 357  	if managed == nil {
 358  		return nil, status.Errorf(codes.FailedPrecondition, "managed ACL not available")
 359  	}
 360  	managedACL := managed.GetManagedACL()
 361  	if managedACL == nil {
 362  		return nil, status.Errorf(codes.FailedPrecondition, "managed ACL database not available")
 363  	}
 364  	if err := managedACL.RemoveBlockedIP(req.Ip); err != nil {
 365  		return nil, status.Errorf(codes.Internal, "failed to unblock IP: %v", err)
 366  	}
 367  	return &orlyaclv1.Empty{}, nil
 368  }
 369  
 370  func (s *ACLService) ListBlockedIPs(ctx context.Context, req *orlyaclv1.Empty) (*orlyaclv1.ListBlockedIPsResponse, error) {
 371  	managed := s.getManagedACL()
 372  	if managed == nil {
 373  		return nil, status.Errorf(codes.FailedPrecondition, "managed ACL not available")
 374  	}
 375  	managedACL := managed.GetManagedACL()
 376  	if managedACL == nil {
 377  		return nil, status.Errorf(codes.FailedPrecondition, "managed ACL database not available")
 378  	}
 379  	blocked, err := managedACL.ListBlockedIPs()
 380  	if err != nil {
 381  		return nil, status.Errorf(codes.Internal, "failed to list blocked IPs: %v", err)
 382  	}
 383  	resp := &orlyaclv1.ListBlockedIPsResponse{}
 384  	for _, b := range blocked {
 385  		resp.Ips = append(resp.Ips, &orlyaclv1.BlockedIP{
 386  			Ip:     b.IP,
 387  			Reason: b.Reason,
 388  			Added:  b.Added.Unix(),
 389  		})
 390  	}
 391  	return resp, nil
 392  }
 393  
 394  func (s *ACLService) AllowKind(ctx context.Context, req *orlyaclv1.AllowKindRequest) (*orlyaclv1.Empty, error) {
 395  	managed := s.getManagedACL()
 396  	if managed == nil {
 397  		return nil, status.Errorf(codes.FailedPrecondition, "managed ACL not available")
 398  	}
 399  	managedACL := managed.GetManagedACL()
 400  	if managedACL == nil {
 401  		return nil, status.Errorf(codes.FailedPrecondition, "managed ACL database not available")
 402  	}
 403  	if err := managedACL.SaveAllowedKind(int(req.Kind)); err != nil {
 404  		return nil, status.Errorf(codes.Internal, "failed to allow kind: %v", err)
 405  	}
 406  	return &orlyaclv1.Empty{}, nil
 407  }
 408  
 409  func (s *ACLService) DisallowKind(ctx context.Context, req *orlyaclv1.KindRequest) (*orlyaclv1.Empty, error) {
 410  	managed := s.getManagedACL()
 411  	if managed == nil {
 412  		return nil, status.Errorf(codes.FailedPrecondition, "managed ACL not available")
 413  	}
 414  	managedACL := managed.GetManagedACL()
 415  	if managedACL == nil {
 416  		return nil, status.Errorf(codes.FailedPrecondition, "managed ACL database not available")
 417  	}
 418  	if err := managedACL.RemoveAllowedKind(int(req.Kind)); err != nil {
 419  		return nil, status.Errorf(codes.Internal, "failed to disallow kind: %v", err)
 420  	}
 421  	return &orlyaclv1.Empty{}, nil
 422  }
 423  
 424  func (s *ACLService) ListAllowedKinds(ctx context.Context, req *orlyaclv1.Empty) (*orlyaclv1.ListAllowedKindsResponse, error) {
 425  	managed := s.getManagedACL()
 426  	if managed == nil {
 427  		return nil, status.Errorf(codes.FailedPrecondition, "managed ACL not available")
 428  	}
 429  	managedACL := managed.GetManagedACL()
 430  	if managedACL == nil {
 431  		return nil, status.Errorf(codes.FailedPrecondition, "managed ACL database not available")
 432  	}
 433  	kinds, err := managedACL.ListAllowedKinds()
 434  	if err != nil {
 435  		return nil, status.Errorf(codes.Internal, "failed to list allowed kinds: %v", err)
 436  	}
 437  	resp := &orlyaclv1.ListAllowedKindsResponse{}
 438  	for _, k := range kinds {
 439  		resp.Kinds = append(resp.Kinds, int32(k))
 440  	}
 441  	return resp, nil
 442  }
 443  
 444  func (s *ACLService) UpdatePeerAdmins(ctx context.Context, req *orlyaclv1.UpdatePeerAdminsRequest) (*orlyaclv1.Empty, error) {
 445  	managed := s.getManagedACL()
 446  	if managed == nil {
 447  		return nil, status.Errorf(codes.FailedPrecondition, "managed ACL not available")
 448  	}
 449  	managed.UpdatePeerAdmins(req.PeerPubkeys)
 450  	return &orlyaclv1.Empty{}, nil
 451  }
 452  
 453  // === Curating ACL Methods ===
 454  
 455  func (s *ACLService) TrustPubkey(ctx context.Context, req *orlyaclv1.TrustPubkeyRequest) (*orlyaclv1.Empty, error) {
 456  	curating := s.getCuratingACL()
 457  	if curating == nil {
 458  		return nil, status.Errorf(codes.FailedPrecondition, "curating ACL not available")
 459  	}
 460  	if err := curating.TrustPubkey(req.Pubkey, req.Note); err != nil {
 461  		return nil, status.Errorf(codes.Internal, "failed to trust pubkey: %v", err)
 462  	}
 463  	return &orlyaclv1.Empty{}, nil
 464  }
 465  
 466  func (s *ACLService) UntrustPubkey(ctx context.Context, req *orlyaclv1.PubkeyRequest) (*orlyaclv1.Empty, error) {
 467  	curating := s.getCuratingACL()
 468  	if curating == nil {
 469  		return nil, status.Errorf(codes.FailedPrecondition, "curating ACL not available")
 470  	}
 471  	if err := curating.UntrustPubkey(req.Pubkey); err != nil {
 472  		return nil, status.Errorf(codes.Internal, "failed to untrust pubkey: %v", err)
 473  	}
 474  	return &orlyaclv1.Empty{}, nil
 475  }
 476  
 477  func (s *ACLService) ListTrustedPubkeys(ctx context.Context, req *orlyaclv1.Empty) (*orlyaclv1.ListTrustedPubkeysResponse, error) {
 478  	curating := s.getCuratingACL()
 479  	if curating == nil {
 480  		return nil, status.Errorf(codes.FailedPrecondition, "curating ACL not available")
 481  	}
 482  	curatingACL := curating.GetCuratingACL()
 483  	if curatingACL == nil {
 484  		return nil, status.Errorf(codes.FailedPrecondition, "curating ACL database not available")
 485  	}
 486  	trusted, err := curatingACL.ListTrustedPubkeys()
 487  	if err != nil {
 488  		return nil, status.Errorf(codes.Internal, "failed to list trusted pubkeys: %v", err)
 489  	}
 490  	resp := &orlyaclv1.ListTrustedPubkeysResponse{}
 491  	for _, t := range trusted {
 492  		resp.Pubkeys = append(resp.Pubkeys, &orlyaclv1.TrustedPubkey{
 493  			Pubkey: t.Pubkey,
 494  			Note:   t.Note,
 495  			Added:  t.Added.Unix(),
 496  		})
 497  	}
 498  	return resp, nil
 499  }
 500  
 501  func (s *ACLService) BlacklistPubkey(ctx context.Context, req *orlyaclv1.BlacklistPubkeyRequest) (*orlyaclv1.Empty, error) {
 502  	curating := s.getCuratingACL()
 503  	if curating == nil {
 504  		return nil, status.Errorf(codes.FailedPrecondition, "curating ACL not available")
 505  	}
 506  	if err := curating.BlacklistPubkey(req.Pubkey, req.Reason); err != nil {
 507  		return nil, status.Errorf(codes.Internal, "failed to blacklist pubkey: %v", err)
 508  	}
 509  	return &orlyaclv1.Empty{}, nil
 510  }
 511  
 512  func (s *ACLService) UnblacklistPubkey(ctx context.Context, req *orlyaclv1.PubkeyRequest) (*orlyaclv1.Empty, error) {
 513  	curating := s.getCuratingACL()
 514  	if curating == nil {
 515  		return nil, status.Errorf(codes.FailedPrecondition, "curating ACL not available")
 516  	}
 517  	if err := curating.UnblacklistPubkey(req.Pubkey); err != nil {
 518  		return nil, status.Errorf(codes.Internal, "failed to unblacklist pubkey: %v", err)
 519  	}
 520  	return &orlyaclv1.Empty{}, nil
 521  }
 522  
 523  func (s *ACLService) ListBlacklistedPubkeys(ctx context.Context, req *orlyaclv1.Empty) (*orlyaclv1.ListBlacklistedPubkeysResponse, error) {
 524  	curating := s.getCuratingACL()
 525  	if curating == nil {
 526  		return nil, status.Errorf(codes.FailedPrecondition, "curating ACL not available")
 527  	}
 528  	curatingACL := curating.GetCuratingACL()
 529  	if curatingACL == nil {
 530  		return nil, status.Errorf(codes.FailedPrecondition, "curating ACL database not available")
 531  	}
 532  	blacklisted, err := curatingACL.ListBlacklistedPubkeys()
 533  	if err != nil {
 534  		return nil, status.Errorf(codes.Internal, "failed to list blacklisted pubkeys: %v", err)
 535  	}
 536  	resp := &orlyaclv1.ListBlacklistedPubkeysResponse{}
 537  	for _, b := range blacklisted {
 538  		resp.Pubkeys = append(resp.Pubkeys, &orlyaclv1.BlacklistedPubkey{
 539  			Pubkey: b.Pubkey,
 540  			Reason: b.Reason,
 541  			Added:  b.Added.Unix(),
 542  		})
 543  	}
 544  	return resp, nil
 545  }
 546  
 547  func (s *ACLService) MarkSpam(ctx context.Context, req *orlyaclv1.MarkSpamRequest) (*orlyaclv1.Empty, error) {
 548  	curating := s.getCuratingACL()
 549  	if curating == nil {
 550  		return nil, status.Errorf(codes.FailedPrecondition, "curating ACL not available")
 551  	}
 552  	curatingACL := curating.GetCuratingACL()
 553  	if curatingACL == nil {
 554  		return nil, status.Errorf(codes.FailedPrecondition, "curating ACL database not available")
 555  	}
 556  	if err := curatingACL.MarkEventAsSpam(req.EventId, req.Pubkey, req.Reason); err != nil {
 557  		return nil, status.Errorf(codes.Internal, "failed to mark spam: %v", err)
 558  	}
 559  	return &orlyaclv1.Empty{}, nil
 560  }
 561  
 562  func (s *ACLService) UnmarkSpam(ctx context.Context, req *orlyaclv1.EventRequest) (*orlyaclv1.Empty, error) {
 563  	curating := s.getCuratingACL()
 564  	if curating == nil {
 565  		return nil, status.Errorf(codes.FailedPrecondition, "curating ACL not available")
 566  	}
 567  	curatingACL := curating.GetCuratingACL()
 568  	if curatingACL == nil {
 569  		return nil, status.Errorf(codes.FailedPrecondition, "curating ACL database not available")
 570  	}
 571  	if err := curatingACL.UnmarkEventAsSpam(req.EventId); err != nil {
 572  		return nil, status.Errorf(codes.Internal, "failed to unmark spam: %v", err)
 573  	}
 574  	return &orlyaclv1.Empty{}, nil
 575  }
 576  
 577  func (s *ACLService) ListSpamEvents(ctx context.Context, req *orlyaclv1.Empty) (*orlyaclv1.ListSpamEventsResponse, error) {
 578  	curating := s.getCuratingACL()
 579  	if curating == nil {
 580  		return nil, status.Errorf(codes.FailedPrecondition, "curating ACL not available")
 581  	}
 582  	curatingACL := curating.GetCuratingACL()
 583  	if curatingACL == nil {
 584  		return nil, status.Errorf(codes.FailedPrecondition, "curating ACL database not available")
 585  	}
 586  	spam, err := curatingACL.ListSpamEvents()
 587  	if err != nil {
 588  		return nil, status.Errorf(codes.Internal, "failed to list spam events: %v", err)
 589  	}
 590  	resp := &orlyaclv1.ListSpamEventsResponse{}
 591  	for _, se := range spam {
 592  		resp.Events = append(resp.Events, &orlyaclv1.SpamEvent{
 593  			EventId: se.EventID,
 594  			Pubkey:  se.Pubkey,
 595  			Reason:  se.Reason,
 596  			Added:   se.Added.Unix(),
 597  		})
 598  	}
 599  	return resp, nil
 600  }
 601  
 602  func (s *ACLService) RateLimitCheck(ctx context.Context, req *orlyaclv1.RateLimitCheckRequest) (*orlyaclv1.RateLimitCheckResponse, error) {
 603  	curating := s.getCuratingACL()
 604  	if curating == nil {
 605  		return nil, status.Errorf(codes.FailedPrecondition, "curating ACL not available")
 606  	}
 607  	allowed, message, err := curating.RateLimitCheck(req.Pubkey, req.Ip)
 608  	if err != nil {
 609  		return nil, status.Errorf(codes.Internal, "failed to check rate limit: %v", err)
 610  	}
 611  	return &orlyaclv1.RateLimitCheckResponse{
 612  		Allowed: allowed,
 613  		Message: message,
 614  	}, nil
 615  }
 616  
 617  func (s *ACLService) ProcessConfigEvent(ctx context.Context, req *orlyaclv1.ConfigEventRequest) (*orlyaclv1.Empty, error) {
 618  	curating := s.getCuratingACL()
 619  	if curating == nil {
 620  		return nil, status.Errorf(codes.FailedPrecondition, "curating ACL not available")
 621  	}
 622  	ev := orlydbv1.ProtoToEvent(req.Event)
 623  	if err := curating.ProcessConfigEvent(ev); err != nil {
 624  		return nil, status.Errorf(codes.Internal, "failed to process config event: %v", err)
 625  	}
 626  	return &orlyaclv1.Empty{}, nil
 627  }
 628  
 629  func (s *ACLService) GetCuratingConfig(ctx context.Context, req *orlyaclv1.Empty) (*orlyaclv1.CuratingConfig, error) {
 630  	curating := s.getCuratingACL()
 631  	if curating == nil {
 632  		return nil, status.Errorf(codes.FailedPrecondition, "curating ACL not available")
 633  	}
 634  	config, err := curating.GetConfig()
 635  	if err != nil {
 636  		return nil, status.Errorf(codes.Internal, "failed to get config: %v", err)
 637  	}
 638  	resp := &orlyaclv1.CuratingConfig{
 639  		ConfigEventId:  config.ConfigEventID,
 640  		ConfigPubkey:   config.ConfigPubkey,
 641  		ConfiguredAt:   config.ConfiguredAt,
 642  		DailyLimit:     int32(config.DailyLimit),
 643  		IpDailyLimit:   int32(config.IPDailyLimit),
 644  		FirstBanHours:  int32(config.FirstBanHours),
 645  		SecondBanHours: int32(config.SecondBanHours),
 646  		KindCategories: config.KindCategories,
 647  		AllowedRanges:  config.AllowedRanges,
 648  	}
 649  	for _, k := range config.AllowedKinds {
 650  		resp.AllowedKinds = append(resp.AllowedKinds, int32(k))
 651  	}
 652  	return resp, nil
 653  }
 654  
 655  func (s *ACLService) IsCuratingConfigured(ctx context.Context, req *orlyaclv1.Empty) (*orlyaclv1.BoolResponse, error) {
 656  	curating := s.getCuratingACL()
 657  	if curating == nil {
 658  		return &orlyaclv1.BoolResponse{Value: false}, nil
 659  	}
 660  	configured, err := curating.IsConfigured()
 661  	if err != nil {
 662  		return nil, status.Errorf(codes.Internal, "failed to check if configured: %v", err)
 663  	}
 664  	return &orlyaclv1.BoolResponse{Value: configured}, nil
 665  }
 666  
 667  func (s *ACLService) ListUnclassifiedUsers(ctx context.Context, req *orlyaclv1.PaginationRequest) (*orlyaclv1.ListUnclassifiedUsersResponse, error) {
 668  	curating := s.getCuratingACL()
 669  	if curating == nil {
 670  		return nil, status.Errorf(codes.FailedPrecondition, "curating ACL not available")
 671  	}
 672  	curatingACL := curating.GetCuratingACL()
 673  	if curatingACL == nil {
 674  		return nil, status.Errorf(codes.FailedPrecondition, "curating ACL database not available")
 675  	}
 676  	// The underlying ListUnclassifiedUsers only takes limit, not offset
 677  	// We'll request limit+offset and skip the first offset items
 678  	limit := int(req.Limit)
 679  	offset := int(req.Offset)
 680  	if limit == 0 {
 681  		limit = 100 // Default limit
 682  	}
 683  	users, err := curatingACL.ListUnclassifiedUsers(limit + offset)
 684  	if err != nil {
 685  		return nil, status.Errorf(codes.Internal, "failed to list unclassified users: %v", err)
 686  	}
 687  	// Apply offset
 688  	if offset > 0 && len(users) > offset {
 689  		users = users[offset:]
 690  	} else if offset > 0 {
 691  		users = nil
 692  	}
 693  	// Apply limit
 694  	if limit > 0 && len(users) > limit {
 695  		users = users[:limit]
 696  	}
 697  	resp := &orlyaclv1.ListUnclassifiedUsersResponse{Total: int32(len(users))}
 698  	for _, u := range users {
 699  		resp.Users = append(resp.Users, &orlyaclv1.UnclassifiedUser{
 700  			Pubkey:     u.Pubkey,
 701  			EventCount: int32(u.EventCount),
 702  			FirstSeen:  u.LastEvent.Format("2006-01-02T15:04:05Z"),
 703  		})
 704  	}
 705  	return resp, nil
 706  }
 707  
 708  func (s *ACLService) GetEventsForPubkey(ctx context.Context, req *orlyaclv1.GetEventsForPubkeyRequest) (*orlyaclv1.EventsForPubkeyResponse, error) {
 709  	curating := s.getCuratingACL()
 710  	if curating == nil {
 711  		return nil, status.Errorf(codes.FailedPrecondition, "curating ACL not available")
 712  	}
 713  	curatingACL := curating.GetCuratingACL()
 714  	if curatingACL == nil {
 715  		return nil, status.Errorf(codes.FailedPrecondition, "curating ACL database not available")
 716  	}
 717  	events, total, err := curatingACL.GetEventsForPubkey(req.Pubkey, int(req.Limit), int(req.Offset))
 718  	if err != nil {
 719  		return nil, status.Errorf(codes.Internal, "failed to get events for pubkey: %v", err)
 720  	}
 721  	resp := &orlyaclv1.EventsForPubkeyResponse{Total: int32(total)}
 722  	for _, ev := range events {
 723  		resp.Events = append(resp.Events, &orlyaclv1.EventSummary{
 724  			Id:        ev.ID,
 725  			Kind:      uint32(ev.Kind),
 726  			Content:   []byte(ev.Content),
 727  			CreatedAt: ev.CreatedAt,
 728  		})
 729  	}
 730  	return resp, nil
 731  }
 732  
 733  func (s *ACLService) DeleteEventsForPubkey(ctx context.Context, req *orlyaclv1.DeleteEventsForPubkeyRequest) (*orlyaclv1.DeleteCountResponse, error) {
 734  	curating := s.getCuratingACL()
 735  	if curating == nil {
 736  		return nil, status.Errorf(codes.FailedPrecondition, "curating ACL not available")
 737  	}
 738  	curatingACL := curating.GetCuratingACL()
 739  	if curatingACL == nil {
 740  		return nil, status.Errorf(codes.FailedPrecondition, "curating ACL database not available")
 741  	}
 742  	count, err := curatingACL.DeleteEventsForPubkey(req.Pubkey)
 743  	if err != nil {
 744  		return nil, status.Errorf(codes.Internal, "failed to delete events for pubkey: %v", err)
 745  	}
 746  	return &orlyaclv1.DeleteCountResponse{Count: int32(count)}, nil
 747  }
 748  
 749  func (s *ACLService) ScanAllPubkeys(ctx context.Context, req *orlyaclv1.Empty) (*orlyaclv1.ScanResultResponse, error) {
 750  	curating := s.getCuratingACL()
 751  	if curating == nil {
 752  		return nil, status.Errorf(codes.FailedPrecondition, "curating ACL not available")
 753  	}
 754  	curatingACL := curating.GetCuratingACL()
 755  	if curatingACL == nil {
 756  		return nil, status.Errorf(codes.FailedPrecondition, "curating ACL database not available")
 757  	}
 758  	result, err := curatingACL.ScanAllPubkeys()
 759  	if err != nil {
 760  		return nil, status.Errorf(codes.Internal, "failed to scan all pubkeys: %v", err)
 761  	}
 762  	return &orlyaclv1.ScanResultResponse{
 763  		TotalPubkeys: int32(result.TotalPubkeys),
 764  		TotalEvents:  int32(result.TotalEvents),
 765  	}, nil
 766  }
 767  
 768  // === Helper Methods ===
 769  
 770  func (s *ACLService) getManagedACL() *acl.Managed {
 771  	for _, i := range acl.Registry.ACLs() {
 772  		if i.Type() == "managed" {
 773  			if managed, ok := i.(*acl.Managed); ok {
 774  				return managed
 775  			}
 776  		}
 777  	}
 778  	return nil
 779  }
 780  
 781  func (s *ACLService) getCuratingACL() *acl.Curating {
 782  	for _, i := range acl.Registry.ACLs() {
 783  		if i.Type() == "curating" {
 784  			if curating, ok := i.(*acl.Curating); ok {
 785  				return curating
 786  			}
 787  		}
 788  	}
 789  	return nil
 790  }
 791  
 792  // === Paid ACL Methods ===
 793  
 794  func (s *ACLService) SubscribePubkey(ctx context.Context, req *orlyaclv1.SubscribeRequest) (*orlyaclv1.Empty, error) {
 795  	paid := s.getPaidACL()
 796  	if paid == nil {
 797  		return nil, status.Errorf(codes.FailedPrecondition, "paid ACL not available")
 798  	}
 799  	expiresAt := time.Unix(req.ExpiresAt, 0)
 800  	if err := paid.Subscribe(req.Pubkey, expiresAt, req.InvoiceHash, req.Alias); err != nil {
 801  		return nil, status.Errorf(codes.Internal, "failed to subscribe: %v", err)
 802  	}
 803  	return &orlyaclv1.Empty{}, nil
 804  }
 805  
 806  func (s *ACLService) UnsubscribePubkey(ctx context.Context, req *orlyaclv1.PubkeyRequest) (*orlyaclv1.Empty, error) {
 807  	paid := s.getPaidACL()
 808  	if paid == nil {
 809  		return nil, status.Errorf(codes.FailedPrecondition, "paid ACL not available")
 810  	}
 811  	if err := paid.Unsubscribe(req.Pubkey); err != nil {
 812  		return nil, status.Errorf(codes.Internal, "failed to unsubscribe: %v", err)
 813  	}
 814  	return &orlyaclv1.Empty{}, nil
 815  }
 816  
 817  func (s *ACLService) IsSubscribed(ctx context.Context, req *orlyaclv1.PubkeyRequest) (*orlyaclv1.BoolResponse, error) {
 818  	paid := s.getPaidACL()
 819  	if paid == nil {
 820  		return nil, status.Errorf(codes.FailedPrecondition, "paid ACL not available")
 821  	}
 822  	return &orlyaclv1.BoolResponse{Value: paid.IsSubscribed(req.Pubkey)}, nil
 823  }
 824  
 825  func (s *ACLService) GetSubscription(ctx context.Context, req *orlyaclv1.PubkeyRequest) (*orlyaclv1.SubscriptionResponse, error) {
 826  	paid := s.getPaidACL()
 827  	if paid == nil {
 828  		return nil, status.Errorf(codes.FailedPrecondition, "paid ACL not available")
 829  	}
 830  	sub, err := paid.GetSubscription(req.Pubkey)
 831  	if err != nil {
 832  		return nil, status.Errorf(codes.Internal, "failed to get subscription: %v", err)
 833  	}
 834  	resp := &orlyaclv1.SubscriptionResponse{
 835  		Pubkey:    sub.PubkeyHex,
 836  		Alias:     sub.Alias,
 837  		ExpiresAt: sub.ExpiresAt.Unix(),
 838  		CreatedAt: sub.CreatedAt.Unix(),
 839  		HasAlias:  sub.Alias != "",
 840  	}
 841  	return resp, nil
 842  }
 843  
 844  func (s *ACLService) ClaimAlias(ctx context.Context, req *orlyaclv1.ClaimAliasRequest) (*orlyaclv1.Empty, error) {
 845  	paid := s.getPaidACL()
 846  	if paid == nil {
 847  		return nil, status.Errorf(codes.FailedPrecondition, "paid ACL not available")
 848  	}
 849  	if err := paid.ClaimAlias(req.Alias, req.Pubkey); err != nil {
 850  		if errors.Is(err, database.ErrAliasTaken) {
 851  			return nil, status.Errorf(codes.AlreadyExists, "alias is already taken")
 852  		}
 853  		return nil, status.Errorf(codes.Internal, "failed to claim alias: %v", err)
 854  	}
 855  	return &orlyaclv1.Empty{}, nil
 856  }
 857  
 858  func (s *ACLService) GetAliasByPubkey(ctx context.Context, req *orlyaclv1.PubkeyRequest) (*orlyaclv1.AliasResponse, error) {
 859  	paid := s.getPaidACL()
 860  	if paid == nil {
 861  		return nil, status.Errorf(codes.FailedPrecondition, "paid ACL not available")
 862  	}
 863  	alias, err := paid.GetAliasByPubkey(req.Pubkey)
 864  	if err != nil {
 865  		return nil, status.Errorf(codes.Internal, "failed to get alias: %v", err)
 866  	}
 867  	return &orlyaclv1.AliasResponse{Alias: alias}, nil
 868  }
 869  
 870  func (s *ACLService) GetPubkeyByAlias(ctx context.Context, req *orlyaclv1.AliasRequest) (*orlyaclv1.PubkeyResponse, error) {
 871  	paid := s.getPaidACL()
 872  	if paid == nil {
 873  		return nil, status.Errorf(codes.FailedPrecondition, "paid ACL not available")
 874  	}
 875  	pubkey, err := paid.GetPubkeyByAlias(req.Alias)
 876  	if err != nil {
 877  		return nil, status.Errorf(codes.Internal, "failed to get pubkey by alias: %v", err)
 878  	}
 879  	return &orlyaclv1.PubkeyResponse{Pubkey: pubkey}, nil
 880  }
 881  
 882  func (s *ACLService) IsAliasTaken(ctx context.Context, req *orlyaclv1.AliasRequest) (*orlyaclv1.BoolResponse, error) {
 883  	paid := s.getPaidACL()
 884  	if paid == nil {
 885  		return nil, status.Errorf(codes.FailedPrecondition, "paid ACL not available")
 886  	}
 887  	taken, err := paid.IsAliasTaken(req.Alias)
 888  	if err != nil {
 889  		return nil, status.Errorf(codes.Internal, "failed to check alias: %v", err)
 890  	}
 891  	return &orlyaclv1.BoolResponse{Value: taken}, nil
 892  }
 893  
 894  // === Helper Methods ===
 895  
 896  func (s *ACLService) getPaidACL() *acl.Paid {
 897  	for _, i := range acl.Registry.ACLs() {
 898  		if i.Type() == "paid" {
 899  			if paid, ok := i.(*acl.Paid); ok {
 900  				return paid
 901  			}
 902  		}
 903  	}
 904  	return nil
 905  }
 906