# Task 02: Config & MongoDB Store ## Objective Build config loading from env vars and MongoDB token storage for OAuth sessions. ## Part A: Config (`internal/config/config.go`) ### Config struct ```go type Config struct { Port string BaseURL string GHLClientID string GHLClientSecret string GHLWebhookPublicKey string // PEM-encoded ECDSA public key GHLConversationProviderID string CastAPIKey string CastAPIURL string CastSenderID string MongoURI string InboundAPIKey string } ``` ### Load function ```go func Load() (*Config, error) ``` - Read all vars from `os.Getenv()` - Validate required fields: `BASE_URL`, `GHL_CLIENT_ID`, `GHL_CLIENT_SECRET`, `GHL_WEBHOOK_PUBLIC_KEY`, `GHL_CONVERSATION_PROVIDER_ID`, `CAST_API_KEY`, `MONGO_URI` - Defaults: `PORT` → `"3002"`, `CAST_API_URL` → `"https://api.cast.ph"` - Return descriptive error listing ALL missing vars (not just the first) ## Part B: MongoDB Store (`internal/store/mongo.go`) ### TokenRecord struct ```go type TokenRecord struct { LocationID string `bson:"location_id"` CompanyID string `bson:"company_id"` AccessToken string `bson:"access_token"` RefreshToken string `bson:"refresh_token"` ExpiresAt time.Time `bson:"expires_at"` InstalledAt time.Time `bson:"installed_at"` UpdatedAt time.Time `bson:"updated_at"` } ``` ### Store struct and methods ```go type Store struct { collection *mongo.Collection } func NewStore(ctx context.Context, uri string) (*Store, error) // Connects to MongoDB, returns Store with "oauth_tokens" collection // Creates unique index on location_id func (s *Store) SaveToken(ctx context.Context, record *TokenRecord) error // Upsert by location_id (insert or replace) func (s *Store) GetToken(ctx context.Context, locationID string) (*TokenRecord, error) // Find by location_id. Return nil, nil if not found. func (s *Store) UpdateToken(ctx context.Context, locationID, accessToken, refreshToken string, expiresAt time.Time) error // Update access_token, refresh_token, expires_at, updated_at for a location func (s *Store) DeleteToken(ctx context.Context, locationID string) error // Remove token on app uninstall func (s *Store) Close(ctx context.Context) error // Disconnect from MongoDB ``` ### Key behaviors - Use `context.Context` on all operations - Set `updated_at` to `time.Now()` on every write - `SaveToken` uses MongoDB `ReplaceOne` with `upsert: true` - `GetToken` returns `(nil, nil)` when not found (not an error) - Connection timeout: 10 seconds - Create index: `{ location_id: 1 }` unique ## Acceptance Criteria - [ ] `go build ./cmd/server/` succeeds - [ ] `Config.Load()` returns error listing all missing required vars - [ ] `Config.Load()` applies defaults for PORT and CAST_API_URL - [ ] `Store.NewStore()` connects to MongoDB with 10s timeout - [ ] `Store.SaveToken()` upserts by location_id - [ ] `Store.GetToken()` returns nil when not found - [ ] `Store.UpdateToken()` updates token fields + updated_at - [ ] `Store.DeleteToken()` removes the record - [ ] Unique index on location_id - [ ] All methods accept context.Context