Initial commit: Custom Start Page application with authentication and DynamoDB storage

This commit is contained in:
2026-02-18 22:06:43 -05:00
commit 7175ff14ba
47 changed files with 7592 additions and 0 deletions

View File

@@ -0,0 +1,165 @@
# Task 2.1 Implementation: User Data Model and DynamoDB Users Table
## Overview
This task implements the User data model and DynamoDB Users table with full CRUD operations.
## Files Created
### 1. User Model (`internal/models/user.go`)
- Defines the `User` struct with all required fields:
- `ID` (user_id): Unique identifier (UUID)
- `Email`: User's email address
- `OAuthProvider`: OAuth provider name (e.g., "google", "github")
- `OAuthID`: OAuth provider's user ID
- `CreatedAt`: Timestamp when user was created
- `UpdatedAt`: Timestamp when user was last updated
### 2. DynamoDB Client (`internal/storage/dynamodb.go`)
- `NewDynamoDBClient`: Creates a new DynamoDB client with optional endpoint override
- `CreateUsersTable`: Creates the Users table with proper schema
- Partition Key: `user_id` (String)
- Billing Mode: Pay-per-request (on-demand)
- Waits for table to be active before returning
### 3. User Storage Layer (`internal/storage/user_storage.go`)
Implements all CRUD operations:
- `CreateUser`: Creates a new user with auto-generated UUID
- `GetUserByID`: Retrieves a user by their ID
- `GetUserByOAuth`: Retrieves a user by OAuth provider and ID
- `UpdateUser`: Updates an existing user (auto-updates UpdatedAt)
- `DeleteUser`: Deletes a user by their ID
### 4. Unit Tests (`internal/storage/user_storage_test.go`)
Comprehensive test coverage:
- `TestCreateUser`: Verifies user creation with all fields
- `TestGetUserByID`: Tests user retrieval by ID
- `TestGetUserByID_NotFound`: Tests error handling for non-existent users
- `TestGetUserByOAuth`: Tests user retrieval by OAuth credentials
- `TestGetUserByOAuth_NotFound`: Tests error handling for OAuth lookup
- `TestUpdateUser`: Verifies user updates and timestamp changes
- `TestDeleteUser`: Tests user deletion
- `TestCreateUser_MultipleUsers`: Verifies multiple users can be created with unique IDs
### 5. Database Initialization (`cmd/init-db/main.go`)
Standalone CLI tool to initialize DynamoDB tables:
```bash
go run cmd/init-db/main.go -endpoint http://localhost:8000
```
### 6. Table Initialization Helper (`cmd/server/init_tables.go`)
Helper function for the main server to initialize tables on startup.
## DynamoDB Schema
### Users Table
```
Table Name: Users
Partition Key: user_id (String)
Billing Mode: Pay-per-request
Attributes:
- user_id: String (UUID)
- email: String
- oauth_provider: String
- oauth_id: String
- created_at: Number (Unix timestamp in nanoseconds)
- updated_at: Number (Unix timestamp in nanoseconds)
```
## Dependencies Added
- `github.com/aws/aws-sdk-go-v2`: AWS SDK for Go v2
- `github.com/aws/aws-sdk-go-v2/config`: AWS configuration
- `github.com/aws/aws-sdk-go-v2/service/dynamodb`: DynamoDB service client
- `github.com/aws/aws-sdk-go-v2/feature/dynamodb/attributevalue`: DynamoDB attribute marshaling
- `github.com/google/uuid`: UUID generation
## Running Tests
### Prerequisites
1. Start DynamoDB Local:
```bash
make db-start
```
Or manually:
```bash
docker-compose up -d dynamodb-local
```
2. Set environment variable (optional):
```bash
export DYNAMODB_ENDPOINT=http://localhost:8000
```
### Run Tests
```bash
make test
# or
go test -v ./internal/storage/...
```
## Usage Example
```go
package main
import (
"context"
"log"
"custom-start-page/internal/storage"
)
func main() {
ctx := context.Background()
// Create DynamoDB client
db, err := storage.NewDynamoDBClient(ctx, "http://localhost:8000")
if err != nil {
log.Fatal(err)
}
// Create Users table
if err := db.CreateUsersTable(ctx); err != nil {
log.Fatal(err)
}
// Create user storage
userStorage := storage.NewUserStorage(db)
// Create a new user
user, err := userStorage.CreateUser(ctx, "user@example.com", "google", "google123")
if err != nil {
log.Fatal(err)
}
log.Printf("Created user: %+v", user)
// Retrieve user by ID
retrievedUser, err := userStorage.GetUserByID(ctx, user.ID)
if err != nil {
log.Fatal(err)
}
log.Printf("Retrieved user: %+v", retrievedUser)
}
```
## Notes
### OAuth Lookup Performance
The `GetUserByOAuth` method currently uses a Scan operation since there's no GSI for `oauth_provider` + `oauth_id`. For production use with many users, consider adding a GSI:
- Partition Key: `oauth_provider`
- Sort Key: `oauth_id`
This would change the query from O(n) scan to O(1) lookup.
### Timestamp Storage
Timestamps are stored as Go `time.Time` which DynamoDB marshals as Unix timestamps in nanoseconds. This provides high precision for audit trails.
## Requirements Validated
- ✓ Requirement 1.1: User authentication foundation
- ✓ Requirement 1.3: OAuth user creation and retrieval
- ✓ Requirement 8.7: User data association
## Next Steps
Task 2.2 will implement the OAuth service using this User model to handle Google OAuth authentication.