mirror of
https://tangled.org/evan.jarrett.net/at-container-registry
synced 2026-05-28 11:00:22 +00:00
actually wrap them in a envvar check
This commit is contained in:
@@ -43,7 +43,7 @@ func main() {
|
||||
|
||||
// Initialize PDS with carstore and keys
|
||||
ctx := context.Background()
|
||||
holdPDS, err = pds.NewHoldPDS(ctx, holdDID, cfg.Server.PublicURL, cfg.Database.Path, cfg.Database.KeyPath)
|
||||
holdPDS, err = pds.NewHoldPDS(ctx, holdDID, cfg.Server.PublicURL, cfg.Database.Path, cfg.Database.KeyPath, cfg.Registration.EnableBlueskyPosts)
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to initialize embedded PDS: %v", err)
|
||||
}
|
||||
@@ -103,7 +103,7 @@ func main() {
|
||||
xrpcHandler = pds.NewXRPCHandler(holdPDS, *s3Service, driver, broadcaster, nil)
|
||||
|
||||
// Create OCI XRPC handler (multipart upload endpoints)
|
||||
ociHandler = oci.NewXRPCHandler(holdPDS, *s3Service, driver, cfg.Server.DisablePresignedURLs, nil)
|
||||
ociHandler = oci.NewXRPCHandler(holdPDS, *s3Service, driver, cfg.Server.DisablePresignedURLs, cfg.Registration.EnableBlueskyPosts, nil)
|
||||
}
|
||||
|
||||
// Setup HTTP routes with chi router
|
||||
|
||||
@@ -32,6 +32,11 @@ type RegistrationConfig struct {
|
||||
// ProfileAvatarURL is the URL to download the avatar image from (from env: HOLD_PROFILE_AVATAR)
|
||||
// If set, the avatar will be downloaded and uploaded as a blob during bootstrap
|
||||
ProfileAvatarURL string `yaml:"profile_avatar_url"`
|
||||
|
||||
// EnableBlueskyPosts controls whether to create Bluesky posts for manifest uploads (from env: HOLD_BLUESKY_POSTS_ENABLED)
|
||||
// If true, creates posts when users push images
|
||||
// Can be overridden per-hold via captain record's enableManifestPosts field
|
||||
EnableBlueskyPosts bool `yaml:"enable_bluesky_posts"`
|
||||
}
|
||||
|
||||
// StorageConfig wraps distribution's storage configuration
|
||||
@@ -96,6 +101,7 @@ func LoadConfigFromEnv() (*Config, error) {
|
||||
cfg.Registration.OwnerDID = os.Getenv("HOLD_OWNER")
|
||||
cfg.Registration.AllowAllCrew = os.Getenv("HOLD_ALLOW_ALL_CREW") == "true"
|
||||
cfg.Registration.ProfileAvatarURL = getEnvOrDefault("HOLD_PROFILE_AVATAR", "https://imgs.blue/evan.jarrett.net/1TpTOdtS60GdJWBYEqtK22y688jajbQ9a5kbYRFtwuqrkBAE")
|
||||
cfg.Registration.EnableBlueskyPosts = os.Getenv("HOLD_BLUESKY_POSTS_ENABLED") == "true"
|
||||
|
||||
// Database configuration (optional - enables embedded PDS)
|
||||
// Note: HOLD_DATABASE_DIR is a directory path, carstore creates db.sqlite3 inside it
|
||||
|
||||
@@ -21,10 +21,11 @@ type XRPCHandler struct {
|
||||
MultipartMgr *MultipartManager // Exported for access in route handlers
|
||||
pds *pds.HoldPDS
|
||||
httpClient pds.HTTPClient
|
||||
enableBlueskyPosts bool
|
||||
}
|
||||
|
||||
// NewXRPCHandler creates a new OCI XRPC handler
|
||||
func NewXRPCHandler(holdPDS *pds.HoldPDS, s3Service s3.S3Service, driver storagedriver.StorageDriver, disablePresignedURLs bool, httpClient pds.HTTPClient) *XRPCHandler {
|
||||
func NewXRPCHandler(holdPDS *pds.HoldPDS, s3Service s3.S3Service, driver storagedriver.StorageDriver, disablePresignedURLs bool, enableBlueskyPosts bool, httpClient pds.HTTPClient) *XRPCHandler {
|
||||
return &XRPCHandler{
|
||||
driver: driver,
|
||||
disablePresignedURLs: disablePresignedURLs,
|
||||
@@ -32,6 +33,7 @@ func NewXRPCHandler(holdPDS *pds.HoldPDS, s3Service s3.S3Service, driver storage
|
||||
s3Service: s3Service,
|
||||
pds: holdPDS,
|
||||
httpClient: httpClient,
|
||||
enableBlueskyPosts: enableBlueskyPosts,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -242,9 +244,9 @@ func (h *XRPCHandler) HandleNotifyManifest(w http.ResponseWriter, r *http.Reques
|
||||
}
|
||||
|
||||
// Check if manifest posts are enabled
|
||||
// TODO: Check captain record enableManifestPosts field
|
||||
// For now, posts are always created
|
||||
postsEnabled := true
|
||||
// Controlled by HOLD_BLUESKY_POSTS_ENABLED environment variable
|
||||
// TODO: Override with captain record enableManifestPosts field if set
|
||||
postsEnabled := h.enableBlueskyPosts
|
||||
|
||||
// Create layer records for each blob
|
||||
layersCreated := 0
|
||||
|
||||
@@ -97,7 +97,7 @@ func setupTestOCIHandler(t *testing.T) (*XRPCHandler, context.Context) {
|
||||
t.Fatalf("Failed to copy shared signing key: %v", err)
|
||||
}
|
||||
|
||||
holdPDS, err := pds.NewHoldPDS(ctx, holdDID, publicURL, dbPath, keyPath)
|
||||
holdPDS, err := pds.NewHoldPDS(ctx, holdDID, publicURL, dbPath, keyPath, false)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create PDS: %v", err)
|
||||
}
|
||||
@@ -126,7 +126,7 @@ func setupTestOCIHandler(t *testing.T) (*XRPCHandler, context.Context) {
|
||||
|
||||
// Create OCI handler with buffered mode (no S3)
|
||||
mockS3 := s3.S3Service{}
|
||||
handler := NewXRPCHandler(holdPDS, mockS3, driver, true, mockClient)
|
||||
handler := NewXRPCHandler(holdPDS, mockS3, driver, true, false, mockClient)
|
||||
|
||||
return handler, ctx
|
||||
}
|
||||
|
||||
@@ -28,7 +28,7 @@ func setupTestPDS(t *testing.T) (*HoldPDS, context.Context) {
|
||||
t.Fatalf("Failed to copy shared signing key: %v", err)
|
||||
}
|
||||
|
||||
pds, err := NewHoldPDS(ctx, "did:web:hold.example.com", "https://hold.example.com", dbPath, keyPath)
|
||||
pds, err := NewHoldPDS(ctx, "did:web:hold.example.com", "https://hold.example.com", dbPath, keyPath, false)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create test PDS: %v", err)
|
||||
}
|
||||
|
||||
@@ -84,7 +84,7 @@ func TestGenerateDIDDocument(t *testing.T) {
|
||||
keyPath := filepath.Join(tmpDir, "signing-key")
|
||||
publicURL := "https://hold.example.com"
|
||||
|
||||
pds, err := NewHoldPDS(ctx, "did:web:hold.example.com", publicURL, dbPath, keyPath)
|
||||
pds, err := NewHoldPDS(ctx, "did:web:hold.example.com", publicURL, dbPath, keyPath, false)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create PDS: %v", err)
|
||||
}
|
||||
@@ -183,7 +183,7 @@ func TestGenerateDIDDocument_WithPort(t *testing.T) {
|
||||
keyPath := filepath.Join(tmpDir, "signing-key")
|
||||
publicURL := "https://hold.example.com:8443"
|
||||
|
||||
pds, err := NewHoldPDS(ctx, "did:web:hold.example.com:8443", publicURL, dbPath, keyPath)
|
||||
pds, err := NewHoldPDS(ctx, "did:web:hold.example.com:8443", publicURL, dbPath, keyPath, false)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create PDS: %v", err)
|
||||
}
|
||||
@@ -213,7 +213,7 @@ func TestMarshalDIDDocument(t *testing.T) {
|
||||
keyPath := filepath.Join(tmpDir, "signing-key")
|
||||
publicURL := "https://hold.example.com"
|
||||
|
||||
pds, err := NewHoldPDS(ctx, "did:web:hold.example.com", publicURL, dbPath, keyPath)
|
||||
pds, err := NewHoldPDS(ctx, "did:web:hold.example.com", publicURL, dbPath, keyPath, false)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create PDS: %v", err)
|
||||
}
|
||||
@@ -261,7 +261,7 @@ func TestGenerateDIDDocument_InvalidURL(t *testing.T) {
|
||||
keyPath := filepath.Join(tmpDir, "signing-key")
|
||||
publicURL := "https://hold.example.com"
|
||||
|
||||
pds, err := NewHoldPDS(ctx, "did:web:hold.example.com", publicURL, dbPath, keyPath)
|
||||
pds, err := NewHoldPDS(ctx, "did:web:hold.example.com", publicURL, dbPath, keyPath, false)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create PDS: %v", err)
|
||||
}
|
||||
|
||||
@@ -30,17 +30,18 @@ func init() {
|
||||
|
||||
// HoldPDS is a minimal ATProto PDS implementation for a hold service
|
||||
type HoldPDS struct {
|
||||
did string
|
||||
PublicURL string
|
||||
carstore carstore.CarStore
|
||||
repomgr *RepoManager
|
||||
dbPath string
|
||||
uid models.Uid
|
||||
signingKey *atcrypto.PrivateKeyK256
|
||||
did string
|
||||
PublicURL string
|
||||
carstore carstore.CarStore
|
||||
repomgr *RepoManager
|
||||
dbPath string
|
||||
uid models.Uid
|
||||
signingKey *atcrypto.PrivateKeyK256
|
||||
enableBlueskyPosts bool
|
||||
}
|
||||
|
||||
// NewHoldPDS creates or opens a hold PDS with SQLite carstore
|
||||
func NewHoldPDS(ctx context.Context, did, publicURL, dbPath, keyPath string) (*HoldPDS, error) {
|
||||
func NewHoldPDS(ctx context.Context, did, publicURL, dbPath, keyPath string, enableBlueskyPosts bool) (*HoldPDS, error) {
|
||||
// Generate or load signing key
|
||||
signingKey, err := GenerateOrLoadKey(keyPath)
|
||||
if err != nil {
|
||||
@@ -95,13 +96,14 @@ func NewHoldPDS(ctx context.Context, did, publicURL, dbPath, keyPath string) (*H
|
||||
}
|
||||
|
||||
return &HoldPDS{
|
||||
did: did,
|
||||
PublicURL: publicURL,
|
||||
carstore: cs,
|
||||
repomgr: rm,
|
||||
dbPath: dbPath,
|
||||
uid: uid,
|
||||
signingKey: signingKey,
|
||||
did: did,
|
||||
PublicURL: publicURL,
|
||||
carstore: cs,
|
||||
repomgr: rm,
|
||||
dbPath: dbPath,
|
||||
uid: uid,
|
||||
signingKey: signingKey,
|
||||
enableBlueskyPosts: enableBlueskyPosts,
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
||||
@@ -23,7 +23,7 @@ func TestNewHoldPDS_NewRepo(t *testing.T) {
|
||||
did := "did:web:hold.example.com"
|
||||
publicURL := "https://hold.example.com"
|
||||
|
||||
pds, err := NewHoldPDS(ctx, did, publicURL, dbPath, keyPath)
|
||||
pds, err := NewHoldPDS(ctx, did, publicURL, dbPath, keyPath, false)
|
||||
if err != nil {
|
||||
t.Fatalf("NewHoldPDS failed: %v", err)
|
||||
}
|
||||
@@ -62,7 +62,7 @@ func TestNewHoldPDS_ExistingRepo(t *testing.T) {
|
||||
publicURL := "https://hold.example.com"
|
||||
|
||||
// Create first PDS instance and bootstrap it
|
||||
pds1, err := NewHoldPDS(ctx, did, publicURL, dbPath, keyPath)
|
||||
pds1, err := NewHoldPDS(ctx, did, publicURL, dbPath, keyPath, false)
|
||||
if err != nil {
|
||||
t.Fatalf("First NewHoldPDS failed: %v", err)
|
||||
}
|
||||
@@ -86,7 +86,7 @@ func TestNewHoldPDS_ExistingRepo(t *testing.T) {
|
||||
pds1.Close()
|
||||
|
||||
// Re-open the same database
|
||||
pds2, err := NewHoldPDS(ctx, did, publicURL, dbPath, keyPath)
|
||||
pds2, err := NewHoldPDS(ctx, did, publicURL, dbPath, keyPath, false)
|
||||
if err != nil {
|
||||
t.Fatalf("Second NewHoldPDS failed: %v", err)
|
||||
}
|
||||
@@ -118,7 +118,7 @@ func TestBootstrap_NewRepo(t *testing.T) {
|
||||
dbPath := filepath.Join(tmpDir, "pds.db")
|
||||
keyPath := filepath.Join(tmpDir, "signing-key")
|
||||
|
||||
pds, err := NewHoldPDS(ctx, "did:web:hold.example.com", "https://hold.example.com", dbPath, keyPath)
|
||||
pds, err := NewHoldPDS(ctx, "did:web:hold.example.com", "https://hold.example.com", dbPath, keyPath, false)
|
||||
if err != nil {
|
||||
t.Fatalf("NewHoldPDS failed: %v", err)
|
||||
}
|
||||
@@ -195,7 +195,7 @@ func TestBootstrap_Idempotent(t *testing.T) {
|
||||
dbPath := filepath.Join(tmpDir, "pds.db")
|
||||
keyPath := filepath.Join(tmpDir, "signing-key")
|
||||
|
||||
pds, err := NewHoldPDS(ctx, "did:web:hold.example.com", "https://hold.example.com", dbPath, keyPath)
|
||||
pds, err := NewHoldPDS(ctx, "did:web:hold.example.com", "https://hold.example.com", dbPath, keyPath, false)
|
||||
if err != nil {
|
||||
t.Fatalf("NewHoldPDS failed: %v", err)
|
||||
}
|
||||
@@ -261,7 +261,7 @@ func TestBootstrap_EmptyOwner(t *testing.T) {
|
||||
dbPath := filepath.Join(tmpDir, "pds.db")
|
||||
keyPath := filepath.Join(tmpDir, "signing-key")
|
||||
|
||||
pds, err := NewHoldPDS(ctx, "did:web:hold.example.com", "https://hold.example.com", dbPath, keyPath)
|
||||
pds, err := NewHoldPDS(ctx, "did:web:hold.example.com", "https://hold.example.com", dbPath, keyPath, false)
|
||||
if err != nil {
|
||||
t.Fatalf("NewHoldPDS failed: %v", err)
|
||||
}
|
||||
@@ -294,7 +294,7 @@ func TestLexiconTypeRegistration(t *testing.T) {
|
||||
dbPath := filepath.Join(tmpDir, "pds.db")
|
||||
keyPath := filepath.Join(tmpDir, "signing-key")
|
||||
|
||||
pds, err := NewHoldPDS(ctx, "did:web:hold.example.com", "https://hold.example.com", dbPath, keyPath)
|
||||
pds, err := NewHoldPDS(ctx, "did:web:hold.example.com", "https://hold.example.com", dbPath, keyPath, false)
|
||||
if err != nil {
|
||||
t.Fatalf("NewHoldPDS failed: %v", err)
|
||||
}
|
||||
@@ -344,7 +344,7 @@ func TestBootstrap_DidWebOwner(t *testing.T) {
|
||||
dbPath := filepath.Join(tmpDir, "pds.db")
|
||||
keyPath := filepath.Join(tmpDir, "signing-key")
|
||||
|
||||
pds, err := NewHoldPDS(ctx, "did:web:hold01.atcr.io", "https://hold01.atcr.io", dbPath, keyPath)
|
||||
pds, err := NewHoldPDS(ctx, "did:web:hold01.atcr.io", "https://hold01.atcr.io", dbPath, keyPath, false)
|
||||
if err != nil {
|
||||
t.Fatalf("NewHoldPDS failed: %v", err)
|
||||
}
|
||||
@@ -406,7 +406,7 @@ func TestBootstrap_MixedDIDs(t *testing.T) {
|
||||
|
||||
// Create hold with did:web
|
||||
holdDID := "did:web:hold.example.com"
|
||||
pds, err := NewHoldPDS(ctx, holdDID, "https://hold.example.com", dbPath, keyPath)
|
||||
pds, err := NewHoldPDS(ctx, holdDID, "https://hold.example.com", dbPath, keyPath, false)
|
||||
if err != nil {
|
||||
t.Fatalf("NewHoldPDS failed: %v", err)
|
||||
}
|
||||
@@ -474,7 +474,7 @@ func TestBootstrap_CrewWithoutCaptain(t *testing.T) {
|
||||
dbPath := filepath.Join(tmpDir, "pds.db")
|
||||
keyPath := filepath.Join(tmpDir, "signing-key")
|
||||
|
||||
pds, err := NewHoldPDS(ctx, "did:web:hold.example.com", "https://hold.example.com", dbPath, keyPath)
|
||||
pds, err := NewHoldPDS(ctx, "did:web:hold.example.com", "https://hold.example.com", dbPath, keyPath, false)
|
||||
if err != nil {
|
||||
t.Fatalf("NewHoldPDS failed: %v", err)
|
||||
}
|
||||
@@ -546,7 +546,7 @@ func TestBootstrap_CaptainWithoutCrew(t *testing.T) {
|
||||
dbPath := filepath.Join(tmpDir, "pds.db")
|
||||
keyPath := filepath.Join(tmpDir, "signing-key")
|
||||
|
||||
pds, err := NewHoldPDS(ctx, "did:web:hold.example.com", "https://hold.example.com", dbPath, keyPath)
|
||||
pds, err := NewHoldPDS(ctx, "did:web:hold.example.com", "https://hold.example.com", dbPath, keyPath, false)
|
||||
if err != nil {
|
||||
t.Fatalf("NewHoldPDS failed: %v", err)
|
||||
}
|
||||
|
||||
@@ -17,6 +17,12 @@ const (
|
||||
// status should be "online" or "offline"
|
||||
// Each call creates a unique post with a TID-based rkey
|
||||
func (p *HoldPDS) SetStatus(ctx context.Context, status string) error {
|
||||
// Check if Bluesky posts are enabled
|
||||
if !p.enableBlueskyPosts {
|
||||
fmt.Printf("Bluesky posts disabled, skipping status post: %s\n", status)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Format the post text with emoji indicator
|
||||
emoji := "🟢"
|
||||
if status == "offline" {
|
||||
|
||||
@@ -42,7 +42,7 @@ func TestStatusPost(t *testing.T) {
|
||||
did := "did:web:test.example.com"
|
||||
publicURL := "https://test.example.com"
|
||||
|
||||
holdPDS, err := NewHoldPDS(ctx, did, publicURL, dbPath, keyPath)
|
||||
holdPDS, err := NewHoldPDS(ctx, did, publicURL, dbPath, keyPath, true)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create test PDS: %v", err)
|
||||
}
|
||||
@@ -276,7 +276,7 @@ func TestMain(m *testing.M) {
|
||||
// Create one shared, bootstrapped PDS for read-only tests
|
||||
// Use in-memory database for speed
|
||||
sharedCtx = context.Background()
|
||||
sharedPDS, err = NewHoldPDS(sharedCtx, "did:web:hold.example.com", "https://hold.example.com", ":memory:", sharedTestKeyPath)
|
||||
sharedPDS, err = NewHoldPDS(sharedCtx, "did:web:hold.example.com", "https://hold.example.com", ":memory:", sharedTestKeyPath, true)
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("Failed to create shared PDS: %v", err))
|
||||
}
|
||||
|
||||
@@ -44,7 +44,7 @@ func setupTestXRPCHandler(t *testing.T) (*XRPCHandler, context.Context) {
|
||||
t.Fatalf("Failed to copy shared signing key: %v", err)
|
||||
}
|
||||
|
||||
pds, err := NewHoldPDS(ctx, "did:web:hold.example.com", "https://hold.example.com", dbPath, keyPath)
|
||||
pds, err := NewHoldPDS(ctx, "did:web:hold.example.com", "https://hold.example.com", dbPath, keyPath, false)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create test PDS: %v", err)
|
||||
}
|
||||
@@ -1377,7 +1377,7 @@ func setupTestXRPCHandlerWithBlobs(t *testing.T) (*XRPCHandler, *mockS3Service,
|
||||
t.Fatalf("Failed to copy shared signing key: %v", err)
|
||||
}
|
||||
|
||||
pds, err := NewHoldPDS(ctx, "did:web:hold.example.com", "https://hold.example.com", dbPath, keyPath)
|
||||
pds, err := NewHoldPDS(ctx, "did:web:hold.example.com", "https://hold.example.com", dbPath, keyPath, false)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create test PDS: %v", err)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user