From 044d408cf81f82463ec6480d8046520ecff0649e Mon Sep 17 00:00:00 2001 From: Evan Jarrett Date: Thu, 18 Dec 2025 11:19:49 -0600 Subject: [PATCH] deployment fixes. add open graph --- .env.hold.example | 9 +++- .tangled/workflows/tests.yml | 2 +- Makefile | 24 +++++++++- cmd/hold/main.go | 10 ++++ deploy/.env.prod.template | 16 ++----- deploy/docker-compose.prod.yml | 7 +-- pkg/appview/jetstream/processor_test.go | 1 + pkg/appview/templates/components/head.html | 39 ++++++++++++++++ pkg/hold/config.go | 54 ++++++++++++++++++++++ 9 files changed, 141 insertions(+), 21 deletions(-) diff --git a/.env.hold.example b/.env.hold.example index a282e79..a490c98 100644 --- a/.env.hold.example +++ b/.env.hold.example @@ -29,8 +29,8 @@ AWS_ACCESS_KEY_ID=your_access_key AWS_SECRET_ACCESS_KEY=your_secret_key # S3 Region -# Examples: us-east-1, us-west-2, eu-west-1 -# For UpCloud: us-chi1, us-nyc1, de-fra1, uk-lon1, sg-sin1 +# For third-party S3 providers, this is ignored when S3_ENDPOINT is set, +# but must be a valid AWS region (e.g., us-east-1) to pass validation. # Default: us-east-1 AWS_REGION=us-east-1 @@ -61,6 +61,11 @@ S3_BUCKET=atcr-blobs # Default: false HOLD_PUBLIC=false +# ATProto relay endpoint for requesting crawl on startup +# This makes the hold's embedded PDS discoverable by the relay network +# Default: https://bsky.network (set to empty string to disable) +# HOLD_RELAY_ENDPOINT=https://bsky.network + # ============================================================================== # Embedded PDS Configuration # ============================================================================== diff --git a/.tangled/workflows/tests.yml b/.tangled/workflows/tests.yml index 30f559e..a17423f 100644 --- a/.tangled/workflows/tests.yml +++ b/.tangled/workflows/tests.yml @@ -6,7 +6,7 @@ when: engine: kubernetes image: golang:1.25-trixie -architecture: arm64 +architecture: amd64 steps: - name: Download and Generate diff --git a/Makefile b/Makefile index 33f06c3..2767c66 100644 --- a/Makefile +++ b/Makefile @@ -2,7 +2,8 @@ # Build targets for the ATProto Container Registry .PHONY: all build build-appview build-hold build-credential-helper build-oauth-helper \ - generate test test-race test-verbose lint clean help install-credential-helper + generate test test-race test-verbose lint clean help install-credential-helper \ + develop develop-detached develop-down .DEFAULT_GOAL := help @@ -80,6 +81,27 @@ install-credential-helper: build-credential-helper ## Install credential helper install -m 755 bin/docker-credential-atcr /usr/local/sbin/docker-credential-atcr @echo "✓ Installed docker-credential-atcr to /usr/local/sbin/" +##@ Docker Targets + +develop: ## Build Docker images and start docker-compose for development + @echo "→ Building Docker images..." + docker-compose build + @echo "→ Starting docker-compose..." + docker-compose up + +develop-detached: ## Build and start docker-compose in detached mode + @echo "→ Building Docker images..." + docker-compose build + @echo "→ Starting docker-compose (detached)..." + docker-compose up -d + @echo "✓ Services started in background" + @echo " AppView: http://localhost:5000" + @echo " Hold: http://localhost:8080" + +develop-down: ## Stop docker-compose services + @echo "→ Stopping docker-compose..." + docker-compose down + ##@ Utility Targets clean: ## Remove built binaries and generated assets diff --git a/cmd/hold/main.go b/cmd/hold/main.go index 3fbeff7..1d14030 100644 --- a/cmd/hold/main.go +++ b/cmd/hold/main.go @@ -179,6 +179,16 @@ func main() { } } + // Request crawl from relay to make PDS discoverable + if cfg.Server.RelayEndpoint != "" { + slog.Info("Requesting crawl from relay", "relay", cfg.Server.RelayEndpoint) + if err := hold.RequestCrawl(cfg.Server.RelayEndpoint, cfg.Server.PublicURL); err != nil { + slog.Warn("Failed to request crawl from relay", "error", err) + } else { + slog.Info("Crawl requested successfully") + } + } + // Wait for signal or server error select { case err := <-serverErr: diff --git a/deploy/.env.prod.template b/deploy/.env.prod.template index 9873160..b079216 100644 --- a/deploy/.env.prod.template +++ b/deploy/.env.prod.template @@ -115,10 +115,10 @@ AWS_ACCESS_KEY_ID= AWS_SECRET_ACCESS_KEY= # S3 Region (for distribution S3 driver) -# UpCloud regions: us-chi1, us-nyc1, de-fra1, uk-lon1, sg-sin1, etc. -# Note: Use AWS_REGION (not S3_REGION) - this is what the hold service expects +# For third-party S3 providers (UpCloud, Storj, Minio), this value is ignored +# when S3_ENDPOINT is set, but must be a valid AWS region to pass validation. # Default: us-east-1 -AWS_REGION=us-chi1 +AWS_REGION=us-east-1 # S3 Bucket Name # Create this bucket in UpCloud Object Storage @@ -134,11 +134,6 @@ S3_BUCKET=atcr # Custom domains break presigned URL generation S3_ENDPOINT=https://6vmss.upcloudobjects.com -# S3 Region Endpoint (alternative to S3_ENDPOINT) -# Use this if your S3 driver requires region-specific endpoint format -# Example: s3.us-chi1.upcloudobjects.com -# S3_REGION_ENDPOINT= - # ============================================================================== # AppView Configuration # ============================================================================== @@ -231,13 +226,12 @@ ATCR_BACKFILL_INTERVAL=1h # ☐ Set HOLD_OWNER (your ATProto DID) # ☐ Set HOLD_DATABASE_DIR (default: /var/lib/atcr-hold) - enables embedded PDS # ☐ Set AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY -# ☐ Set AWS_REGION (e.g., us-chi1) # ☐ Set S3_BUCKET (created in UpCloud Object Storage) -# ☐ Set S3_ENDPOINT (UpCloud endpoint or custom domain) +# ☐ Set S3_ENDPOINT (UpCloud bucket endpoint, e.g., https://6vmss.upcloudobjects.com) # ☐ Configured DNS records: # - A record: atcr.io → server IP # - A record: hold01.atcr.io → server IP -# - CNAME: blobs.atcr.io → [bucket].us-chi1.upcloudobjects.com +# - CNAME: blobs.atcr.io → [bucket].upcloudobjects.com # ☐ Disabled Cloudflare proxy (gray cloud, not orange) # ☐ Waited for DNS propagation (check with: dig atcr.io) # diff --git a/deploy/docker-compose.prod.yml b/deploy/docker-compose.prod.yml index 18a720f..21d6e5a 100644 --- a/deploy/docker-compose.prod.yml +++ b/deploy/docker-compose.prod.yml @@ -109,10 +109,9 @@ services: # S3/UpCloud Object Storage configuration AWS_ACCESS_KEY_ID: ${AWS_ACCESS_KEY_ID:-} AWS_SECRET_ACCESS_KEY: ${AWS_SECRET_ACCESS_KEY:-} - AWS_REGION: ${AWS_REGION:-us-chi1} + AWS_REGION: ${AWS_REGION:-us-east-1} S3_BUCKET: ${S3_BUCKET:-atcr-blobs} S3_ENDPOINT: ${S3_ENDPOINT:-} - S3_REGION_ENDPOINT: ${S3_REGION_ENDPOINT:-} # Logging ATCR_LOG_LEVEL: ${ATCR_LOG_LEVEL:-debug} @@ -160,8 +159,6 @@ configs: # Preserve original host header header_up Host {host} header_up X-Real-IP {remote_host} - header_up X-Forwarded-For {remote_host} - header_up X-Forwarded-Proto {scheme} } # Enable compression @@ -183,8 +180,6 @@ configs: # Preserve original host header header_up Host {host} header_up X-Real-IP {remote_host} - header_up X-Forwarded-For {remote_host} - header_up X-Forwarded-Proto {scheme} } # Enable compression diff --git a/pkg/appview/jetstream/processor_test.go b/pkg/appview/jetstream/processor_test.go index 9132227..c54237d 100644 --- a/pkg/appview/jetstream/processor_test.go +++ b/pkg/appview/jetstream/processor_test.go @@ -70,6 +70,7 @@ func setupTestDB(t *testing.T) *sql.DB { platform_os TEXT, platform_variant TEXT, platform_os_version TEXT, + is_attestation BOOLEAN DEFAULT FALSE, reference_index INTEGER NOT NULL, PRIMARY KEY(manifest_id, reference_index) ); diff --git a/pkg/appview/templates/components/head.html b/pkg/appview/templates/components/head.html index 5957327..86ed9b2 100644 --- a/pkg/appview/templates/components/head.html +++ b/pkg/appview/templates/components/head.html @@ -2,6 +2,45 @@ + + {{ if .ViewedUser }} + + + + {{ if .ViewedUser.Avatar }} + + {{ else }} + + {{ end }} + + + {{ else if .Repository }} + + + {{ if .Repository.Description }} + + {{ else }} + + {{ end }} + {{ if .Repository.IconURL }} + + {{ else if .Owner.Avatar }} + + {{ else }} + + {{ end }} + + + {{ else }} + + + + + + + {{ end }} + + diff --git a/pkg/hold/config.go b/pkg/hold/config.go index 50ee770..05693ff 100644 --- a/pkg/hold/config.go +++ b/pkg/hold/config.go @@ -6,7 +6,11 @@ package hold import ( + "bytes" + "encoding/json" "fmt" + "net/http" + "net/url" "os" "path/filepath" "time" @@ -67,6 +71,10 @@ type ServerConfig struct { // DisablePresignedURLs forces proxy mode even with S3 configured (for testing) (from env: DISABLE_PRESIGNED_URLS) DisablePresignedURLs bool `yaml:"disable_presigned_urls"` + // RelayEndpoint is the ATProto relay URL to request crawl from on startup (from env: HOLD_RELAY_ENDPOINT) + // If empty, no crawl request is made. Default: https://bsky.network + RelayEndpoint string `yaml:"relay_endpoint"` + // ReadTimeout for HTTP requests ReadTimeout time.Duration `yaml:"read_timeout"` @@ -103,6 +111,7 @@ func LoadConfigFromEnv() (*Config, error) { cfg.Server.Public = os.Getenv("HOLD_PUBLIC") == "true" cfg.Server.TestMode = os.Getenv("TEST_MODE") == "true" cfg.Server.DisablePresignedURLs = os.Getenv("DISABLE_PRESIGNED_URLS") == "true" + cfg.Server.RelayEndpoint = getEnvOrDefault("HOLD_RELAY_ENDPOINT", "https://bsky.network") cfg.Server.ReadTimeout = 5 * time.Minute // Increased for large blob uploads cfg.Server.WriteTimeout = 5 * time.Minute // Increased for large blob uploads @@ -180,3 +189,48 @@ func getEnvOrDefault(key, defaultValue string) string { } return defaultValue } + +// RequestCrawl sends a crawl request to the ATProto relay for the given hostname. +// This makes the hold's PDS discoverable by the relay network. +func RequestCrawl(relayEndpoint, publicURL string) error { + if relayEndpoint == "" { + return nil // No relay configured, skip + } + + // Extract hostname from public URL + parsed, err := url.Parse(publicURL) + if err != nil { + return fmt.Errorf("failed to parse public URL: %w", err) + } + hostname := parsed.Host + + // Build the request URL + requestURL := relayEndpoint + "/xrpc/com.atproto.sync.requestCrawl" + + // Create request body + body := map[string]string{"hostname": hostname} + bodyJSON, err := json.Marshal(body) + if err != nil { + return fmt.Errorf("failed to marshal request body: %w", err) + } + + // Make the request + client := &http.Client{Timeout: 10 * time.Second} + req, err := http.NewRequest("POST", requestURL, bytes.NewReader(bodyJSON)) + if err != nil { + return fmt.Errorf("failed to create request: %w", err) + } + req.Header.Set("Content-Type", "application/json") + + resp, err := client.Do(req) + if err != nil { + return fmt.Errorf("failed to send request: %w", err) + } + defer resp.Body.Close() + + if resp.StatusCode < 200 || resp.StatusCode >= 300 { + return fmt.Errorf("relay returned status %d", resp.StatusCode) + } + + return nil +}