Skip to content

Commit 1b5b7b1

Browse files
committed
Add package readme files
1 parent ee4f61f commit 1b5b7b1

21 files changed

Lines changed: 599 additions & 97 deletions

File tree

app/readme.md

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
# app
2+
3+
Lifecycle management for application components.
4+
5+
## Usage
6+
7+
```go
8+
// Components implement optional interfaces
9+
type MyService struct{}
10+
11+
func (s *MyService) Start(ctx context.Context) error { return nil }
12+
func (s *MyService) Stop(ctx context.Context) error { return nil }
13+
func (s *MyService) RegisterRoutes(r chi.Router) {
14+
r.Get("/health", s.Health)
15+
}
16+
17+
// Setup discovers capabilities automatically
18+
starts, stops, registrars := app.Setup(ctx, router,
19+
dbPool,
20+
&myService,
21+
&anotherService,
22+
)
23+
24+
// Start executes in order, auto-rollback on failure
25+
if err := app.Start(ctx, log, starts, stops, registrars, router); err != nil {
26+
log.Fatal(err)
27+
}
28+
29+
// Shutdown in reverse order (LIFO)
30+
app.Shutdown(srv, log, stops)
31+
```
32+
33+
## API
34+
35+
```go
36+
type Startable interface {
37+
Start(context.Context) error
38+
}
39+
40+
type Stoppable interface {
41+
Stop(context.Context) error
42+
}
43+
44+
type RouteRegistrar interface {
45+
RegisterRoutes(chi.Router)
46+
}
47+
```
48+
49+
Components implement the interfaces they need. Setup inspects and groups them.

auth/readme.md

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
# auth
2+
3+
Session-based authentication service.
4+
5+
## Usage
6+
7+
```go
8+
// Create service with your Queries implementation
9+
svc := auth.NewService(queries, cfg, log)
10+
11+
// Signup
12+
user, err := svc.Signup(ctx, "user@example.com", "password123")
13+
14+
// Signin (returns session with token)
15+
session, err := svc.Signin(ctx, "user@example.com", "password123")
16+
17+
// Validate session (e.g., in middleware)
18+
user, err := svc.ValidateSession(ctx, sessionToken)
19+
20+
// Signout
21+
svc.Signout(ctx, sessionToken)
22+
```
23+
24+
## API
25+
26+
```go
27+
type Queries interface {
28+
CreateUser(ctx context.Context, id, email, passwordHash string, createdAt, updatedAt time.Time) (*User, error)
29+
GetUserByEmail(ctx context.Context, email string) (*User, error)
30+
GetUserByID(ctx context.Context, id string) (*User, error)
31+
CreateSession(ctx context.Context, id, userID, token string, expiresAt, createdAt time.Time) (*Session, error)
32+
GetSessionByToken(ctx context.Context, token string) (*Session, error)
33+
DeleteSession(ctx context.Context, sessionID string) error
34+
DeleteExpiredSessions(ctx context.Context) error
35+
}
36+
```
37+
38+
Implement with sqlc or manually. See `middleware.go` for HTTP middleware and `context.go` for request context helpers.

config/readme.md

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
# config
2+
3+
Application configuration from YAML, env vars, and flags.
4+
5+
## Usage
6+
7+
```go
8+
cfg, err := config.Load("config.yaml", "MYAPP_", os.Args)
9+
if err != nil {
10+
log.Fatal(err)
11+
}
12+
13+
if err := cfg.Validate(); err != nil {
14+
log.Fatal(err)
15+
}
16+
17+
// Access config
18+
fmt.Println(cfg.Server.Port)
19+
fmt.Println(cfg.Database.ConnectionString())
20+
```
21+
22+
Precedence (highest to lowest):
23+
1. Flags (`--database.host=x`)
24+
2. Env vars (`MYAPP_DATABASE_HOST=x`)
25+
3. YAML file
26+
4. Defaults
27+
28+
## Notes
29+
30+
Static configuration at startup. For dynamic runtime configuration, see `settings/`.

crypto/readme.md

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
# crypto
2+
3+
Cryptographic utilities: AES-256-GCM encryption, Argon2id hashing, HMAC, TOTP.
4+
5+
## Usage
6+
7+
```go
8+
// AES-256-GCM encryption (e.g., for PII)
9+
ciphertext, iv, tag, err := crypto.EncryptEmail(email, key)
10+
plaintext, err := crypto.DecryptEmail(ciphertext, iv, tag, key)
11+
12+
// HMAC for deterministic lookups
13+
hash := crypto.ComputeLookupHash(email, signingKey)
14+
15+
// Argon2id password hashing
16+
salt, _ := crypto.GenerateSalt()
17+
hash := crypto.HashPassword(password, salt)
18+
ok := crypto.VerifyPassword(password, hash, salt)
19+
20+
// Secure random tokens
21+
token, _ := crypto.GenerateSecureToken(32)
22+
```
23+
24+
For TOTP/MFA, see `totp.go`. For PASETO tokens, see `tokens.go`.

db/readme.md

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
# db
2+
3+
PostgreSQL connection with lifecycle management.
4+
5+
## Usage
6+
7+
```go
8+
//go:embed migrations/*.sql
9+
var migrations embed.FS
10+
11+
database := db.New(migrations, "postgres", cfg, log)
12+
13+
// Implements app.Startable/Stoppable
14+
if err := database.Start(ctx); err != nil {
15+
log.Fatal(err)
16+
}
17+
defer database.Stop(ctx)
18+
19+
// Use the connection
20+
sqlDB := database.GetDB()
21+
```
22+
23+
Creates schema automatically if `cfg.Database.Schema` is set.
24+
25+
For migrations, see `migrate.go`.

fake/readme.md

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
# fake
2+
3+
Test doubles for hatmax interfaces.
4+
5+
## Usage
6+
7+
```go
8+
// Fake mailer records all sent messages
9+
fm := fake.NewMailer()
10+
fm.WithOutput(os.Stdout) // print emails to stdout
11+
fm.WithValidation() // fail on invalid messages
12+
13+
// Use in tests
14+
svc := NewService(fm)
15+
svc.SendWelcome(ctx, user)
16+
17+
// Assert
18+
if fm.SendCount() != 1 { t.Error("expected 1 email") }
19+
if !fm.HasMessageTo("user@example.com") { t.Error("wrong recipient") }
20+
if !fm.HasMessageWithSubject("Welcome") { t.Error("wrong subject") }
21+
22+
msg := fm.LastMessage()
23+
fm.Reset() // clear for next test
24+
```

image/readme.md

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
# image
2+
3+
Image storage and processing with variants.
4+
5+
## Usage
6+
7+
```go
8+
// Create store (local or S3)
9+
store := local.NewStore("/var/uploads", "https://cdn.example.com")
10+
store := s3.NewStore(s3Client, bucket, "https://cdn.example.com")
11+
12+
// Store image
13+
err := store.Put(ctx, "images/photo.jpg", reader)
14+
15+
// Get image
16+
reader, err := store.Get(ctx, "images/photo.jpg")
17+
18+
// Get URL
19+
url := store.URL("images/photo.jpg")
20+
21+
// Process variants
22+
processor := stdprocessor.New()
23+
variants := []image.Variant{image.Large, image.Medium, image.Thumbnail}
24+
for _, v := range variants {
25+
resized, _ := processor.Resize(original, v.Width, v.Height)
26+
store.Put(ctx, v.Path(basePath), resized)
27+
}
28+
```
29+
30+
## API
31+
32+
```go
33+
type Store interface {
34+
Put(ctx context.Context, path string, data io.Reader) error
35+
Get(ctx context.Context, path string) (io.ReadCloser, error)
36+
Delete(ctx context.Context, path string) error
37+
URL(path string) string
38+
}
39+
```
40+
41+
Implementations: `local/`, `s3/`. Processor: `stdprocessor/`.

log/readme.md

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
# log
2+
3+
Structured logging on top of slog.
4+
5+
## Usage
6+
7+
```go
8+
log := log.NewLogger(cfg)
9+
10+
log.Info("server started")
11+
log.Infof("listening on %s", port)
12+
log.Error("connection failed")
13+
14+
// With context
15+
reqLog := log.With("request_id", reqID, "user_id", userID)
16+
reqLog.Info("processing request")
17+
```
18+
19+
Levels: `debug`, `info`, `error`. Configured via `cfg.Log.Level`.
20+
21+
JSON output if `LOG_FORMAT=json`, human-readable text by default.
22+
23+
## API
24+
25+
```go
26+
type Logger interface {
27+
Debug(v ...any)
28+
Debugf(format string, a ...any)
29+
Info(v ...any)
30+
Infof(format string, a ...any)
31+
Error(v ...any)
32+
Errorf(format string, a ...any)
33+
With(args ...any) Logger
34+
}
35+
```
36+
37+
For tests: `log.NewNoopLogger()` or `log.NewTestLogger("debug")`.

mailer/readme.md

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
# mailer
2+
3+
Email delivery with multiple providers.
4+
5+
## Usage
6+
7+
```go
8+
// Create mailer (SMTP, SendGrid, SES, or Noop)
9+
mailer := mailer.NewSMTP(smtpConfig)
10+
mailer := mailer.NewSendGrid(apiKey, from)
11+
mailer := mailer.NewSES(awsConfig, from)
12+
mailer := mailer.NewNoop() // for tests
13+
14+
// Send email
15+
msg := &mailer.Message{
16+
From: mailer.Address{Email: "noreply@example.com", Name: "My App"},
17+
To: []mailer.Address{{Email: "user@example.com"}},
18+
Subject: "Welcome",
19+
HTML: "<h1>Hello</h1>",
20+
Text: "Hello",
21+
}
22+
23+
if err := mailer.Send(ctx, msg); err != nil { ... }
24+
```
25+
26+
## API
27+
28+
```go
29+
type Mailer interface {
30+
Send(ctx context.Context, msg *Message) error
31+
}
32+
```

middleware/readme.md

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
# middleware
2+
3+
HTTP middleware for chi router.
4+
5+
## Usage
6+
7+
```go
8+
// Apply default stack (RequestID, RealIP, Logger, Recoverer)
9+
r := chi.NewRouter()
10+
for _, mw := range middleware.DefaultStack() {
11+
r.Use(mw)
12+
}
13+
14+
// Internal services only (adds IP restriction)
15+
for _, mw := range middleware.DefaultInternal() {
16+
r.Use(mw)
17+
}
18+
19+
// Individual middleware
20+
r.Use(middleware.RequestID)
21+
r.Use(middleware.RequireRole(model.RoleAdmin))
22+
r.Use(middleware.RateLimit(100, time.Minute))
23+
```
24+
25+
See: `requestid.go`, `roles.go`, `ratelimit.go`, `stack.go`.

0 commit comments

Comments
 (0)