A community based topic aggregation platform built on atproto
at main 6.0 kB view raw
1// setup_dev_aggregator.go - Creates a local test aggregator on the local PDS 2// 3// This script creates an aggregator account on the local PDS for development testing. 4// After running, you'll need to: 5// 1. Register the aggregator via OAuth UI 6// 2. Generate an API key via the createApiKey endpoint 7// 8// Usage: go run scripts/setup_dev_aggregator.go 9package main 10 11import ( 12 "bytes" 13 "context" 14 "database/sql" 15 "encoding/json" 16 "fmt" 17 "io" 18 "log" 19 "net/http" 20 21 _ "github.com/lib/pq" 22) 23 24const ( 25 PDSURL = "http://localhost:3001" 26 DatabaseURL = "postgres://dev_user:dev_password@localhost:5435/coves_dev?sslmode=disable" 27) 28 29type CreateAccountRequest struct { 30 Email string `json:"email"` 31 Handle string `json:"handle"` 32 Password string `json:"password"` 33} 34 35type CreateAccountResponse struct { 36 DID string `json:"did"` 37 Handle string `json:"handle"` 38 AccessJWT string `json:"accessJwt"` 39} 40 41type CreateSessionRequest struct { 42 Identifier string `json:"identifier"` 43 Password string `json:"password"` 44} 45 46type CreateSessionResponse struct { 47 DID string `json:"did"` 48 Handle string `json:"handle"` 49 AccessJWT string `json:"accessJwt"` 50} 51 52func main() { 53 ctx := context.Background() 54 55 // Configuration 56 handle := "test-aggregator.local.coves.dev" 57 email := "test-aggregator@example.com" 58 password := "test-password-12345" 59 displayName := "Test Aggregator (Dev)" 60 61 log.Printf("Setting up dev aggregator: %s", handle) 62 63 // Connect to database 64 db, err := sql.Open("postgres", DatabaseURL) 65 if err != nil { 66 log.Fatalf("Failed to connect to database: %v", err) 67 } 68 defer db.Close() 69 70 // Step 1: Try to create account on PDS (or get existing session) 71 log.Printf("Creating account on PDS: %s", PDSURL) 72 73 var did string 74 75 // First try to create account 76 createResp, err := createAccount(handle, email, password) 77 if err != nil { 78 log.Printf("Account creation failed (may already exist): %v", err) 79 log.Printf("Trying to create session with existing account...") 80 81 // Try to login instead 82 sessionResp, err := createSession(handle, password) 83 if err != nil { 84 log.Fatalf("Failed to create session: %v", err) 85 } 86 did = sessionResp.DID 87 log.Printf("Logged in as existing account: %s", did) 88 } else { 89 did = createResp.DID 90 log.Printf("Created new account: %s", did) 91 } 92 93 // Step 2: Check if already in users table 94 var existingHandle string 95 err = db.QueryRowContext(ctx, "SELECT handle FROM users WHERE did = $1", did).Scan(&existingHandle) 96 if err == nil { 97 log.Printf("User already exists in users table: %s", existingHandle) 98 } else { 99 // Insert into users table 100 log.Printf("Inserting user into users table...") 101 _, err = db.ExecContext(ctx, ` 102 INSERT INTO users (did, handle, pds_url) 103 VALUES ($1, $2, $3) 104 ON CONFLICT (did) DO UPDATE SET handle = $2 105 `, did, handle, PDSURL) 106 if err != nil { 107 log.Fatalf("Failed to insert user: %v", err) 108 } 109 } 110 111 // Step 3: Check if already in aggregators table 112 var existingAggDID string 113 err = db.QueryRowContext(ctx, "SELECT did FROM aggregators WHERE did = $1", did).Scan(&existingAggDID) 114 if err == nil { 115 log.Printf("Aggregator already exists in aggregators table") 116 } else { 117 // Insert into aggregators table 118 log.Printf("Inserting aggregator into aggregators table...") 119 recordURI := fmt.Sprintf("at://%s/social.coves.aggregator.declaration/self", did) 120 recordCID := "dev-placeholder-cid" 121 122 _, err = db.ExecContext(ctx, ` 123 INSERT INTO aggregators (did, display_name, description, record_uri, record_cid, created_at, indexed_at) 124 VALUES ($1, $2, $3, $4, $5, NOW(), NOW()) 125 `, did, displayName, "Development test aggregator", recordURI, recordCID) 126 if err != nil { 127 log.Fatalf("Failed to insert aggregator: %v", err) 128 } 129 } 130 131 fmt.Println() 132 fmt.Println("========================================") 133 fmt.Println(" DEV AGGREGATOR ACCOUNT CREATED") 134 fmt.Println("========================================") 135 fmt.Println() 136 fmt.Printf(" DID: %s\n", did) 137 fmt.Printf(" Handle: %s\n", handle) 138 fmt.Printf(" Password: %s\n", password) 139 fmt.Println() 140 fmt.Println(" Next steps:") 141 fmt.Println(" 1. Start Coves server: make run") 142 fmt.Println(" 2. Authenticate as this account via OAuth UI") 143 fmt.Println(" 3. Call POST /xrpc/social.coves.aggregator.createApiKey") 144 fmt.Println(" 4. Save the API key and add to aggregators/kagi-news/.env") 145 fmt.Println() 146 fmt.Println("========================================") 147} 148 149func createAccount(handle, email, password string) (*CreateAccountResponse, error) { 150 reqBody := CreateAccountRequest{ 151 Email: email, 152 Handle: handle, 153 Password: password, 154 } 155 156 body, _ := json.Marshal(reqBody) 157 resp, err := http.Post(PDSURL+"/xrpc/com.atproto.server.createAccount", "application/json", bytes.NewReader(body)) 158 if err != nil { 159 return nil, fmt.Errorf("request failed: %w", err) 160 } 161 defer resp.Body.Close() 162 163 respBody, _ := io.ReadAll(resp.Body) 164 if resp.StatusCode != http.StatusOK { 165 return nil, fmt.Errorf("status %d: %s", resp.StatusCode, string(respBody)) 166 } 167 168 var result CreateAccountResponse 169 if err := json.Unmarshal(respBody, &result); err != nil { 170 return nil, fmt.Errorf("failed to parse response: %w", err) 171 } 172 173 return &result, nil 174} 175 176func createSession(identifier, password string) (*CreateSessionResponse, error) { 177 reqBody := CreateSessionRequest{ 178 Identifier: identifier, 179 Password: password, 180 } 181 182 body, _ := json.Marshal(reqBody) 183 resp, err := http.Post(PDSURL+"/xrpc/com.atproto.server.createSession", "application/json", bytes.NewReader(body)) 184 if err != nil { 185 return nil, fmt.Errorf("request failed: %w", err) 186 } 187 defer resp.Body.Close() 188 189 respBody, _ := io.ReadAll(resp.Body) 190 if resp.StatusCode != http.StatusOK { 191 return nil, fmt.Errorf("status %d: %s", resp.StatusCode, string(respBody)) 192 } 193 194 var result CreateSessionResponse 195 if err := json.Unmarshal(respBody, &result); err != nil { 196 return nil, fmt.Errorf("failed to parse response: %w", err) 197 } 198 199 return &result, nil 200}